Android BadTokenException: Unable to add window, is your activity running

25 July 2013 By Nithya Vasudevan 24,218 views 2 Comments
19 Flares Twitter 1 Facebook 6 Google+ 12 19 Flares ×

I faced this exception in one of my application and after a long research I found a solution for this problem.
This exception occurs when the app is trying to notify the user from the background thread (AsyncTask) by opening a Dialog.

If you are trying to modify the UI from background thread (usually from onPostExecute() of AsyncTask) and if the activity enters finishing stage i.e.) explicitly calling finish(), user pressing home or back button or activity clean up made by Android then you get this error.

android.view.WindowManager$BadTokenException: Unable to add window — token [email protected] is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:585)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:326)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
at android.view.Window$LocalWindowManager.addView(Window.java:547)
at android.app.Dialog.show(Dialog.java:277)
com.ibc.activityasyncdemo.MainActivity$LongTask.onPostExecute(MainActivity.java:72)
com.ibc.activityasyncdemo.MainActivity$LongTask.onPostExecute(MainActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:631)
at android.os.AsyncTask.access$600(AsyncTask.java:177)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:644)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)

Let us first create this scenario which causes the exception to be thrown, by using the following code. Here I want to show an AlertDialog in onPostExecute method of my AsyncTask. I pass my activity’s reference to the task’s constructor using “this”, storing it in an instance and using it to create a dialog.

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {
	private LongTask task;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		task = new LongTask(this);
		task.execute((Void) null);
		/*To produce this exception either call finish() or 
		use device back button when activity starts.*/
		//finish(); //Uncomment this line
	}
	
	private static class LongTask extends AsyncTask<Void, Void, String> {
		Activity activity;

		public LongTask(MainActivity mainActivity) {
			super();
			this.activity = mainActivity;
		}

		@Override
		protected String doInBackground(Void... params) {
			for (int i = 0; i < Integer.MAX_VALUE; i++) {
			}
			return "Hello";
		}

		@Override
		protected void onPostExecute(String result) {
			AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
			alertDialog.setTitle(result);
			alertDialog.setMessage("On post execute");
			alertDialog.setCancelable(false);
			// Setting OK Button
			alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK",
					new DialogInterface.OnClickListener() {
						public void onClick(DialogInterface dialog,
								int which) {
							dialog.dismiss();
						}
					});
			alertDialog.show();
		}
	}
}

If the activity finishes (either by calling finish() or if user presses back/home button) before the task completes, alertDialog.show() throws android.view.WindowManager$BadTokenException: Unable to add window — token [email protected] is not valid; is your activity running?.

The reason for this exception is that, as the exception message says, the activity has finished but you are trying to display a dialog with a context of the finished activity. Since there is no window for the dialog to display the android runtime throws this exception.

To solve this problem, we can use isFinishing() method which is called by Android to check whether this activity is in the process of finishing: be it explicit finish() call or activity clean up made by Android. By using this method it is very easy to avoid opening dialog from background thread when activity is finishing.

Also maintain a weak reference for the activity (and not a strong reference so that activity can be destroyed once not needed) and check if the activity is not finishing before performing any UI using this activity reference (i.e. showing a dialog).

Weak references are useful for mappings that should have their entries removed automatically once they are not referenced any more (from outside).

import java.lang.ref.WeakReference;
import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.util.Log;
import android.view.Menu;

public class MainActivity extends Activity {
	private LongTask task;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		task = new LongTask(this);
		task.execute((Void) null);
		
		//finish();
	}

	private static class LongTask extends AsyncTask<Void, Void, String> {

		private final WeakReference<MainActivity> mainActivityWeakRef;
		//Activity activity;
		

		public LongTask(MainActivity mainActivity) {
			super();
			this.mainActivityWeakRef = new WeakReference<MainActivity>(mainActivity);
			//this.activity = mainActivity;
		}

		@Override
		protected String doInBackground(Void... params) {
			for (int i = 0; i < Integer.MAX_VALUE; i++) {
			}
			return "Hello";
		}

		@Override
		protected void onPostExecute(String result) {
			Log.d("onpostexecute", (mainActivityWeakRef.get() != null) + "");
			if (mainActivityWeakRef.get() != null && !mainActivityWeakRef.get().isFinishing()) {
				
				AlertDialog alertDialog = new AlertDialog.Builder(mainActivityWeakRef.get()).create();
				alertDialog.setTitle(result);
				alertDialog.setMessage("On post execute");
				alertDialog.setCancelable(false);
				alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK",
						new DialogInterface.OnClickListener() {
							public void onClick(DialogInterface dialog,
									int which) {
								dialog.dismiss();
							}
						});

				alertDialog.show();
			}
		}
	}
}

To check this, uncomment the finish() statement in onCreate() and run this program. The dialog will not pop up because there is no reference to the activity since it has been finished.

Tags: , , , , ,

  • http://www.heqingbao.com/ HeQingbao

    onPostExecute method should be in the main thread is called.

  • Stephen Joe

    Couldn’t there be a race condition on line 47?
    Where the activity is not finishing during the if check, but will get destroyed right before line 48 executes. Although unlikely, I think it will still lead to the same problem.