During my last project I’ve encountered two problems which caused my app to crash which I did not expect to happen because the exact same code was working within other activities. These crashes were triggered by simple things like a press on the search button of the mobile phone or the creation of a dialog. When analyzing this behavior I’ve identified a nested TabHost View (a TabHost containing another TabHost which contains the Activity) as the cause for the crashes.

One TabHost contains another TabHost. The last TabHost contains the actual content.The problematic setup is shown in the picture to the left. As you can see the outer TabHost contains a Tab which Activity itself is the inner TabHost. The content of this TabHost will be placed between both tabs. So this is a design which doesn’t seem to be so out of the world.

In the following I’m going to point out the two problems and the solutions which I’ve found so that you might be able to solve similar problems. To demonstrate the problems I’ve created an example project which causes the errors and one which contains the proposed solutions where the functions are working.

You can browse or download the source code of the example Apps at our SVN repository at Google Code (http://code.google.com/p/android-nested-tabhost-problems-example/) or use svn to check the project out

Application is crashing when clicking on the Device Search Button

When the activity is nested within two TabHosts a click on the search button, which normally should display the devices default quick search, will lead to a crash caused by the java.lang.IllegalArgumentException: no ident exception:

E/AndroidRuntime(24249): java.lang.IllegalArgumentException: no ident
E/AndroidRuntime(24249):        at android.app.Activity.ensureSearchManager(Activity.java:3459)
E/AndroidRuntime(24249):        at android.app.Activity.startSearch(Activity.java:2608)
E/AndroidRuntime(24249):        at android.app.Activity.onSearchRequested(Activity.java:2572)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow.launchDefaultSearch(PhoneWindow.java:2482)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow.onKeyUp(PhoneWindow.java:1372)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1636)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.widget.TabHost.dispatchKeyEvent(TabHost.java:275)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1655)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1102)
E/AndroidRuntime(24249):        at android.app.Activity.dispatchKeyEvent(Activity.java:2038)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1631)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.widget.TabHost.dispatchKeyEvent(TabHost.java:275)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:747)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1655)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1102)
E/AndroidRuntime(24249):        at android.app.Activity.dispatchKeyEvent(Activity.java:2038)
E/AndroidRuntime(24249):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1631)
E/AndroidRuntime(24249):        at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2368)
E/AndroidRuntime(24249):        at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2338)
E/AndroidRuntime(24249):        at android.view.ViewRoot.handleMessage(ViewRoot.java:1641)
E/AndroidRuntime(24249):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(24249):        at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(24249):        at android.app.ActivityThread.main(ActivityThread.java:4363)
E/AndroidRuntime(24249):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(24249):        at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(24249):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
E/AndroidRuntime(24249):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
E/AndroidRuntime(24249):        at dalvik.system.NativeStart.main(Native Method)
I/Process ( 1283): Sending signal. PID: 24249 SIG: 3

Application is crashing when creating a Dialog

In the example I’m using the following code to create a dialog when a button is pressed:

public void showDialog(View v) {
	final AlertDialog.Builder builder = new AlertDialog.Builder(this);
    	builder.setMessage(getString(R.string.dialogMessage));
    	builder.create().show();
}

This will lead to the java.lang.IllegalStateException: Could not execute method of the activity exception:

W/WindowManager( 1283): Attempted to add application window with unknown token android.os.BinderProxy@45308bb0.  Aborting.
D/AndroidRuntime(25168): Shutting down VM
W/dalvikvm(25168): threadid=3: thread exiting with uncaught exception (group=0x4001b170)
E/AndroidRuntime(25168): Uncaught handler: thread main exiting due to uncaught exception
E/AndroidRuntime(25168): java.lang.IllegalStateException: Could not execute method of the activity
E/AndroidRuntime(25168):        at android.view.View$1.onClick(View.java:2031)
E/AndroidRuntime(25168):        at android.view.View.performClick(View.java:2364)
E/AndroidRuntime(25168):        at android.view.View.onTouchEvent(View.java:4179)
E/AndroidRuntime(25168):        at android.widget.TextView.onTouchEvent(TextView.java:6591)
E/AndroidRuntime(25168):        at android.view.View.dispatchTouchEvent(View.java:3709)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
E/AndroidRuntime(25168):        at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
E/AndroidRuntime(25168):        at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
E/AndroidRuntime(25168):        at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
E/AndroidRuntime(25168):        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
E/AndroidRuntime(25168):        at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
E/AndroidRuntime(25168):        at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(25168):        at android.os.Looper.loop(Looper.java:123)
E/AndroidRuntime(25168):        at android.app.ActivityThread.main(ActivityThread.java:4363)
E/AndroidRuntime(25168):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(25168):        at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(25168):        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
E/AndroidRuntime(25168):        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
E/AndroidRuntime(25168):        at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime(25168): Caused by: java.lang.reflect.InvocationTargetException
E/AndroidRuntime(25168):        at com.appsolut.example.NestedTabhostInnerContentActivity.showDialog(NestedTabhostInnerContentActivity.java:18)
E/AndroidRuntime(25168):        at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(25168):        at java.lang.reflect.Method.invoke(Method.java:521)
E/AndroidRuntime(25168):        at android.view.View$1.onClick(View.java:2026)
E/AndroidRuntime(25168):        ... 39 more
E/AndroidRuntime(25168): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token android.app.LocalActivityManager$LocalActivityRecord@44ce9c60 is not valid; is your activity running?
E/AndroidRuntime(25168):        at android.view.ViewRoot.setView(ViewRoot.java:468)
E/AndroidRuntime(25168):        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
E/AndroidRuntime(25168):        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
E/AndroidRuntime(25168):        at android.view.Window$LocalWindowManager.addView(Window.java:424)
E/AndroidRuntime(25168):        at android.app.Dialog.show(Dialog.java:239)
E/AndroidRuntime(25168):        ... 43 more
I/Process ( 1283): Sending signal. PID: 25168 SIG: 3

This will also happen when the application context is used (getApplicationContext()) to create the dialog. However, when this code is used in an activity which is placed in no or just one TabHost it will work without any problems.

Solutions

For the problem of dialog creation the correct context for the current activity must be determined. For this I’ve created the method getActivityContext(). This method will determine the correct context for the Activity. If the parent is a TabActivity the correct context which should be used is the context of the TabActivity. Otherwise the Activities context is sufficient. The code looks as follows:

public Activity getActivityContext() {
	if (getParent() instanceof TabActivity) {
		return getParent();
	} else {
		return this;
	}
}

This correct activity context can now be used to solve the search button problem. For this I’ve overridden the Activity method onSearchRequested(). This method will now use the correct context to either redirect the call to the parent TabHost or by calling its own super. onSearchRequested() method if the Activity is not contained in a TabHost.

public boolean onSearchRequested() {
	Activity context = getActivityContext();
	if (context == this) {
		return super.onSearchRequested();
	} else {
		return context.onSearchRequested();
	}
}

I would recommend to add these methods to a base class which extends Activity and which will be used within your project because normally you have more than one Activity within a TabHost. This is done in the example in the BaseActivity class.

by Kevin Kratzer