tags:

views:

3736

answers:

5

Hi

I am using the showDialog and dismissDialog methods to show progress dialogs in my app. Moved from creating the dialog and calling show() on it to using this in order to save state when changing orientation.

But when I change the orientation from portrait->landscape->portrait, the dismissDialog no longer dismisses the dialog. The dialog stay there all the time and I need to press the back button for it to disappear.

Any reason why it would behave that way?

Thanks.

Edit

To overcome this issue, I tried adding a removeDialog in onDestroy so that the dialog is not created/displayed twice and before orientation change, the dialog is removed. Tried adding log statements and see what happens

05-21 12:35:14.064: DEBUG/MyClass(193): *************callingShowDialog
05-21 12:35:14.064: DEBUG/MyClass(193): *************onCreareDialog

05-21 12:35:15.385: DEBUG/MyClass(193): *************onSaveInstanceState
05-21 12:35:15.415: DEBUG/MyClass(193): *************onDestroy

05-21 12:35:15.585: DEBUG/MyClass(193): *************callingShowDialog
05-21 12:35:15.585: DEBUG/MyClass(193): *************onCreareDialog
05-21 12:35:15.715: DEBUG/MyClass(193): *************onCreareDialog
05-21 12:35:17.214: DEBUG/MyClass(193): *************onSaveInstanceState
05-21 12:35:17.214: DEBUG/MyClass(193): *************onDestroy

05-21 12:35:17.275: ERROR/WindowManager(193): android.view.WindowLeaked: Activity com.android.MyClass has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@43362088 that was originally added here

05-21 12:35:17.395: DEBUG/MyClass(193): *************callingShowDialog
05-21 12:35:17.395: DEBUG/MyClass(193): *************onCreareDialog
05-21 12:35:17.475: DEBUG/MyClass(193): *************onCreareDialog

If we see here, initially when the activity is displayed, the onCreateDialog is called once and on changing the orientation, onSaveInstanceState and onDestroy are called.

But after that, onCreateDialog is called twice (once by a call to showDialog which I make, but why the 2nd time?) and this happens every time I change the orientation hence forth.

Any idea why that happens?

Thanks again

A: 

Please be aware that every time you change the orientation, a new instance of your Activity (and consequently of your Dialog via onCreateDialog) is created. You can verify this by adding a log statement in either constructor.

Although difficult to say without having a glance at your code, I'm guessing you're calling dismissDialog on an older instance of your Activity.

Consider the following Activity that simply shows an empty Dialog when a button is clicked and starts a TimerTask to dismiss it after 10 seconds:

public class Test extends Activity {

    private Dialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);  
     setContentView(R.layout.test);
     Button btn = (Button) findViewById(R.id.testButton);
     btn.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
       new Timer().schedule(new TimerTask() {
        @Override public void run() { dialog.dismiss(); }
       }, 10000);
       Test.this.showDialog(1);
      }
     });
    }

    @Override
    protected Dialog onCreateDialog(int id) {
     if(id == 1) {
      if(dialog == null) { dialog = new Dialog(this); }
      return dialog;
     }
     return super.onCreateDialog(id);
    }
}

This code has a problem similar to the one you're describing: If - during the 10 seconds - the orientation is changed, the Dialog is never dismissed. Why? Because at the time the TimerTask is run, a whole different set of Test and Dialog instances has been created (during orientation change) and the TimerTask's dialog instance variable references a Test instance that is different from the one that is currently active on the screen.

Obviously there are several ways to deal with this issue (mostly depending on your application), one quick (dirty?) way to fix the above code would be to simply make the dialog field static:

private static Dialog dialog;
Josef
Thanks Josef. But as you said that fix is actually a hack! I am thinking of using Services instead. Till the time the service is running, I show the dialog. Is that the right way to go about this?
lostInTransit
It really depends on the application. But you're right, (very) long running tasks should definitely be put into services.
Josef
+1  A: 

I finally found a solution that works - without the use of services.

Here's the link. Hope it helps someone.

lostInTransit
+2  A: 

I recently ran into this problem as well. I have an application with many activities all needing to access global objects (db connections, lists of data, etc) and so I overrode the Application class. To that end, I realized all I needed was a reference to the latest instance of the progress dialog in my runnable that I was using to dismiss the dialog. This is what I came up with and it gracefully survives phone reorientations:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    addPreferencesFromResource(R.xml.preferences);

    mHandler = new Handler();
    mApplication = (MyApplication)getApplication();

    mFileStorage = (CheckBoxPreference)getPreferenceScreen().findPreference("fileStorage");

    mProgressDialog = new ProgressDialog(this);

    mProgressDialog.setTitle("Location Update");
    mProgressDialog.setMessage("Moving databases to new storage location.");
    mProgressDialog.setIndeterminate(true);
    mProgressDialog.setCancelable(false);
    mProgressDialog.setCanceledOnTouchOutside(false);

    mApplication.setProgressDialog(mProgressDialog);
}

@Override
protected void onResume() {
    super.onResume();

    mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
    mPreferences.registerOnSharedPreferenceChangeListener(this);

    if (sUpdateThread != null && sUpdateThread.isAlive()) {
        mProgressDialog.show();
    }
}

@Override
protected void onPause() {
    super.onPause();

    mPreferences.unregisterOnSharedPreferenceChangeListener(this);

    if (sUpdateThread != null && sUpdateThread.isAlive()) {
        mProgressDialog.hide();
        mApplication.setProgressDialog(null);
    }
}

....random stuff here....

private Runnable showProgressRunnable = new Runnable() {
    public void run() {
        mProgressDialog.show();
    }
};

private Runnable hideProgressRunnable = new Runnable() {
    public void run() {
        if (mApplication.getProgressDialog() != null) {
            mApplication.getProgressDialog().dismiss();
        }
    }
};

My thread does an mHandler.post(showProgressRunnable) when the task starts and then does an mHandler.post(hideProgressRunnable) when the task finishes. Since the reference to the latest ProgressDialog is now stored in the Application class, I can close it out reliably since I am no longer holding on to an old object reference.

Link for posterity: http://developer.android.com/reference/android/app/Application.html

MattC
A: 

After searching through all kinds of forums, i just did the following:

I call the showDialog(MyDialogID) AND dismissDialog(MyDialogId) out of the handleMessage(myMessage) Method from my Handler.

Before that i called showDialog in my onCreate()-Method and tried to dismiss the dialog in handle-Message. Why this solved the problem, i cant tell.

paskster
A: 

I've run into this when showing a dialog in the onCreate override of the Activity. Try moving the code that causes the dialog to be shown into the onPostCreate override instead. This seems to work for me.

sakamoto