tags:

views:

410

answers:

4

I have an activity that starts AsyncTask and shows progress dialog for the duration of operation. The activity is declared NOT be recreated by rotation or keyboard slide.

    <activity android:name=".MyActivity" 
              android:label="@string/app_name"
              android:configChanges="keyboardHidden|orientation"
              >
        <intent-filter>
        </intent-filter>
    </activity>

Once task completed, I dissmiss dialog, but on some phones (framework: 1.5, 1.6) such error is thrown:

java.lang.IllegalArgumentException: View not attached to window manager
    at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:356)
    at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:201)
    at android.view.Window$LocalWindowManager.removeView(Window.java:400)
    at android.app.Dialog.dismissDialog(Dialog.java:268)
    at android.app.Dialog.access$000(Dialog.java:69)
    at android.app.Dialog$1.run(Dialog.java:103)
    at android.app.Dialog.dismiss(Dialog.java:252)
    at xxx.onPostExecute(xxx$1.java:xxx)

My code is:

final Dialog dialog = new AlertDialog.Builder(context)
    .setTitle("Processing...")
    .setCancelable(true)
    .create();

final AsyncTask<MyParams, Object, MyResult> task = new AsyncTask<MyParams, Object, MyResult>() {

    @Override
    protected MyResult doInBackground(MyParams... params) {
        // Long operation goes here
    }

    @Override
    protected void onPostExecute(MyResult result) {
        dialog.dismiss();
        onCompletion(result);
    }
};

task.execute(...);

dialog.setOnCancelListener(new OnCancelListener() {
    @Override
    public void onCancel(DialogInterface arg0) {
        task.cancel(false);
    }
});

dialog.show();

From what I have read (http://bend-ing.blogspot.com/2008/11/properly-handle-progress-dialog-in.html) and seen in Android sources, it looks like the only possible situation to get that exception is when activity was destroyed. But as I have mentioned, I forbid activity recreation for basic events.

So any suggestions are very appreciated.

A: 

What you might want to do is use a handler in you actual activity and use a background thread that can pass messages to the handler which can in turn handle these messages and control the dialogs.

It will look something like this. Change it to suit your needs.

    public class MainActivity extends Activity {

    private ProgressThread progressThread;
    private static AlertDialog mDialog;

    public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.location);

// code for starting the thread and showing dialog
        AlertDialog.Builder builder;
        Context ctx = this;     
        LayoutInflater inflater = (LayoutInflater)ctx.getSystemService(LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.your_dialog_layout, (ViewGroup)findViewById(R.id.your_layout_id));
        builder = new AlertDialog.Builder(ctx);
        builder.setView(layout);
        mDialog = builder.create();
        mDialog.show();
        MyHandler handler = new MyHandler();
        progressThread = new ProgressThread(handler);
        progressThread.start();
   }

        private class MyHandler extends Handler{
            public void handleMessage(Message msg){
                int total = msg.getData().getInt("total");
                if(total == 100){
                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    builder.setMessage("Your message here");
                    builder.show();
                }
            }
        }

       private class ProgressThread extends Thread{
            Handler mHandler;

            ProgressThread(Handler h){
                mHandler = h;
            }

            public synchronized void run(){
                int count = 0;
                while(count <= 100){
                    try{
                        Thread.sleep(100);
         // Do your background tasks here
                    }catch(InterruptedException e){
                        Log.e("ERROR: ", "Thread Interrupted");
                    }
                    Message msg = mHandler.obtainMessage();
                    Bundle b = new Bundle();
                    b.putInt("total", count);
                    msg.setData(b);
                    mHandler.sendMessage(msg);
                }
            }
    }

I did not do any syntax checking but am posting snippets from my code. So please check for syntactical errors. Hope it helps you.

achie
According to docs: AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
alex2k8
ditto! I am fairly certain that AsyncTask was created to allow interaction w/UI thread and that the progress methods such as onPostExecute() are run on the UI thread
Hamy
+1  A: 

I think your code is correct unlike the other answer suggested. onPostExecute will run on the UI thread. That's the whole point of AsyncTask - you don't have to worry about calling runOnUiThread or deal with handlers. Furthermore, according to the docs, dismiss() can be safely called from any thread (not sure they made this the exception).

Perhaps it's a timing issue where dialog.dismiss() is getting called after the activity is no longer displayed?

What about testing what happens if you comment out the setOnCancelListener and then exit the activity while the background task is running? Then your onPostExecute will try to dismiss an already dismissed dialog. If the app crashes you can probably just check if the dialog is open before dismissing it.

I'm having the exact same problem so I'm going to try it out in code.

Brandon
I also looked into dimiss() code, and indeed, it can be safely called from any thread. BTW, I have a problem with testing, as this issue happens on users phones, and I was never able to reproduce by myself :-( So trying to figure out by analyzing code... According to timing. I was thinking on this, but can't imagine a situation how the Activity can be closed before Dialog. If BACK pressed, than it will cancel Dialog first. And the activity automatic recreation is forbidden by manifest file, but may be it still can be recreated some how? Let me know if you find some thing!
alex2k8
+1  A: 

I am having this same exact problem as well. Seems to happen only on some phones, intermittently (Droid and DroidX from what I've seen), here is a stack trace:

  java.lang.IllegalArgumentException: View not attached to window manager
  at android.view.WindowManagerImpl.findViewLocked(WindowManagerImpl.java:355)
  at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:200)
  at android.view.Window$LocalWindowManager.removeView(Window.java:432)
  at android.app.Dialog.dismissDialog(Dialog.java:280)
  at android.app.Dialog.access$000(Dialog.java:73)
  at android.app.Dialog$1.run(Dialog.java:109)
  at android.app.Dialog.dismiss(Dialog.java:264)
  at com.realtynode.liarliar.LiarLiar$CAnalyzeTask.onPostExecute(LiarLiar.java:796)
  at com.realtynode.liarliar.LiarLiar$CAnalyzeTask.onPostExecute(LiarLiar.java:1)
  at android.os.AsyncTask.finish(AsyncTask.java:417)
  at android.os.AsyncTask.access$300(AsyncTask.java:127)
  at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:123)
  at android.app.ActivityThread.main(ActivityThread.java:4363)
  at java.lang.reflect.Method.invokeNative(Method.java:-2)
  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(NativeStart.java:-2)
Yahma
com.realtynode.liarLiar? Nice naming ;)
Hamy
+1  A: 

alex,

I could be wrong here, but I suspect that multiple phones 'in the wild' have a bug that causes them to switch orientation on applications that are marked as statically oriented. This happens quite a bit on my personal phone, and on many of the test phones our group uses (including droid, n1, g1, hero). Typically an app marked as statically oriented (perhaps vertically) will lay itself out for a second or two using a horizontal orientation, and then immediately switch back. End result is that even though you don't want your app to switch orientation, you have to be prepared that it may. I don't know under what exact conditions this behavior can be reproduced, I don't know if it is specific to a version of Android. All I know is that I have seen it happen plenty of times :(

I would recommend using the solution provided in the link you posted that suggests overriding the Activity onCreateDialog method and letting the Android OS manage the lifecycle of your Dialogs. It looks to me like even though you don't want your activity to switch orientations, it is switching orientation somewhere. You can try to track down a method that will always prevent orientation switching, but I am trying to tell you that I personally don't believe there is a foolproof way that works on all current Android phones in the market.

Hamy