AlertDialog causes leaks when activity finishes - android

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.

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.

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.

IllegalStateException while showing DialogFragment after back from AppCompatActivity

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!

App is crashing after Home is pressed. Unable to pause - Derived class did not call super.onSaveInstanceState()

When I press the Home button, the app should be paused, save all state and work fine.
Instead I get this error:
java.lang.RuntimeException: Unable to
pause activity
{be.test.tester/be.test.tester.DataScreen}:
java.lang.IllegalStateException:
Derived class did not call
super.onSaveInstanceState() at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3641)
at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3598)
at
android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3574)
at
android.app.ActivityThread.access$2500(ActivityThread.java:136)
at
android.app.ActivityThread$H.handleMessage(ActivityThread.java:2186)
at
android.os.Handler.dispatchMessage(Handler.java:99)
at
android.os.Looper.loop(Looper.java:143)
at
android.app.ActivityThread.main(ActivityThread.java:5068)
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:858)
at
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
at
dalvik.system.NativeStart.main(Native
Method)
Caused by:
java.lang.IllegalStateException:
Derived class did not call
super.onSaveInstanceState() at
android.view.View.dispatchSaveInstanceState(View.java:6087)
at
android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:1207)
at
android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:1207)
at
android.view.View.saveHierarchyState(View.java:6068)
at
com.android.internal.policy.impl.PhoneWindow.saveHierarchyState(PhoneWindow.java:1475)
at
android.app.Activity.onSaveInstanceState(Activity.java:1106)
at
android.app.Activity.performSaveInstanceState(Activity.java:1056)
at
android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1289)
at
android.app.ActivityThread.performPauseActivity(ActivityThread.java:3623)
... 12 more
My activity reacts on touch:
public class DataScreen extends Activity implements OnGestureListener{
I'm getting some extra's from the intent:
totUsage = Integer.parseInt(getIntent().getStringExtra("extraTotUsage"));
limit = Integer.parseInt(getIntent().getStringExtra("extraLimit"));
Bundle bundle = getIntent().getExtras();
mylist = (ArrayList<HashMap<String, String>>) bundle.get("extraMyList");
A custom view is showing data (canvas). When you scroll on screen, data changes in custom view (set, get method) and redraws itself.
I don't really manage the onSaveInstanceState here, don't really know if I have to.
My app is onTop of the stack, because of:
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
I don't understand the error.
You should override onSaveInstanceState and call its super method like shown below. At least it helped me with the same problem.
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
// your stuff or nothing
}
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// your stuff or nothing
}
I've just been dealing with this problem. In my app the main activity allows a second activity to be called to display info/instructions etc, it also has a surface view that draws the runtime element of the app.
The main activity has a variable to hold the surface view, if that variable hasn't been set before the info/instructions activity is called I get this error. I simply moved the surface view variable assignment into the main activity onCreate and all is well.
It is also possible that one uses a custom preference (e.g. custom dialog preference) that does not call "super.onSavedInstanceState()" which leads to the same error.

Categories

Resources