Llamar openOptionsMenu () en XE16 causa WindowManager.BadTokenException
Tengo una aplicación GDK que funcionó bien en XE12, pero ahora se bloquea en XE16 después de la transición a GDK: 19. En particular, llamar a openOptionsMenu()
en una Actividad (en este caso, para abrir un menú de opciones en una Live Card) causa BadTokenExceptions.
Salida Logcat:
- ¿Por qué el VoiceListener basado en Glass / GDK sólo captura VoiceCommand una vez en XE16?
- Google Glass GDK - ¿Cuándo utilizar Actividad vs Tarjetas?
- Cómo iniciar una actividad normal en Google Glass
- Cómo reflejar la pantalla de Google Glass a PC / Mac?
- Glassware auth: android.accounts.OperationCanceledException "No se permiten las credenciales de uso compartido: cancelación."
04-16 03:36:43.197: E/AndroidRuntime(2465): FATAL EXCEPTION: main 04-16 03:36:43.197: E/AndroidRuntime(2465): Process: com.voidstar.glass.sample.pinDrop, PID: 2465 04-16 03:36:43.197: E/AndroidRuntime(2465): java.lang.RuntimeException: Unable to resume activity {com.voidstar.glass.sample.pinDrop/com.voidstar.glass.sample.pinDrop.MenuActivity}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2828) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2857) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2290) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.access$800(ActivityThread.java:138) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1236) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.os.Handler.dispatchMessage(Handler.java:102) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.os.Looper.loop(Looper.java:149) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.main(ActivityThread.java:5061) 04-16 03:36:43.197: E/AndroidRuntime(2465): at java.lang.reflect.Method.invokeNative(Native Method) 04-16 03:36:43.197: E/AndroidRuntime(2465): at java.lang.reflect.Method.invoke(Method.java:515) 04-16 03:36:43.197: E/AndroidRuntime(2465): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794) 04-16 03:36:43.197: E/AndroidRuntime(2465): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:610) 04-16 03:36:43.197: E/AndroidRuntime(2465): at dalvik.system.NativeStart.main(Native Method) 04-16 03:36:43.197: E/AndroidRuntime(2465): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running? 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.view.ViewRootImpl.setView(ViewRootImpl.java:561) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:259) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69) 04-16 03:36:43.197: E/AndroidRuntime(2465): at com.android.internal.policy.impl.PhoneWindow.openPanel(PhoneWindow.java:693) 04-16 03:36:43.197: E/AndroidRuntime(2465): at com.android.internal.policy.impl.PhoneWindow.openPanel(PhoneWindow.java:555) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.Activity.openOptionsMenu(Activity.java:2878) 04-16 03:36:43.197: E/AndroidRuntime(2465): at com.voidstar.glass.sample.pinDrop.MenuActivity.onResume(MenuActivity.java:71) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1194) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.Activity.performResume(Activity.java:5316) 04-16 03:36:43.197: E/AndroidRuntime(2465): at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2818) 04-16 03:36:43.197: E/AndroidRuntime(2465): ... 12 more
Método en servicio que enlaza la actividad a la tarjeta activa:
@Override public int onStartCommand(Intent intent, int flags, int startId) { // This method is called whenever the Glassware is invoked via voice commands or the OK Glass menu. if (mLiveCard == null) { Log.d(TAG, "Connecting mLocationManager"); Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_COARSE); PinDropLocationListener listener = new PinDropLocationListener(); locationListeners.add(listener); mLocationManager.requestSingleUpdate(criteria, listener, null); mLiveCard = new LiveCard(getBaseContext(), LIVE_CARD_TAG); mLiveCard.setViews(new RemoteViews(getPackageName(), R.layout.activity_waiting)); mLiveCard.attach(this); // Prevent this Service from being killed to free up memory Intent menuIntent = new Intent(this, MenuActivity.class); // Since menus can only be attached to Activities, we create an activity to own and launch the menu. menuIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); mLiveCard.setAction(PendingIntent.getActivity(this, 0, menuIntent, 0)); // This Intent will be fired whenever the LiveCard is tapped. Log.d(TAG, "Publishing LiveCard"); mLiveCard.publish(PublishMode.REVEAL); // Add the LiveCard to the Timeline and switch to it Log.d(TAG, "Done publishing LiveCard"); } else { mLiveCard.navigate(); // Switch to the app if it's already running } return START_STICKY; // No idea what this does. Your guess is as good as mine. }
Y la problemática Actividad:
/** * Activity showing the options menu. */ public class MenuActivity extends Activity { // This is technically an Immersion! // Because Services have no UI, we need to open this Activity, which in turn opens its menu! PinDropService.MenuBinder mBinder; private static String TAG = "PinDropMenu"; boolean hasLocation; /* * Links this Activity to the Service that spawned it, so the Menu can send and receive information */ private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { if (service instanceof PinDropService.MenuBinder) { mBinder = (PinDropService.MenuBinder)service; hasLocation = mBinder.hasLocation(); Log.d(TAG, hasLocation ? "Received has location" : "Received no location"); //openOptionsMenu(); } } @Override public void onServiceDisconnected(ComponentName name) {} }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); bindService(new Intent(this, PinDropService.class), mConnection, 0); } @Override public void onResume() { super.onResume(); openOptionsMenu(); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.pindropmenu, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { if (!hasLocation) { menu.findItem(R.id.directions).setVisible(false); menu.findItem(R.id.remember).setVisible(false); } else { menu.findItem(R.id.directions).setVisible(true); menu.findItem(R.id.remember).setVisible(true); } return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection. switch (item.getItemId()) { case R.id.directions: mBinder.startNavigation(); return true; case R.id.remember: mBinder.addToTimeline(); // TODO: Add Mirror functionality! return true; case R.id.stop: // IT IS CRITICALLY IMPORTANT TO ADD THIS OR THE GLASSWARE CAN'T BE KILLED IN USERSPACE! stopService(new Intent(this, PinDropService.class)); return true; default: return super.onOptionsItemSelected(item); } } @Override public void onOptionsMenuClosed(Menu menu) { // Nothing else to do, closing the Activity. finish(); } @Override public void onStop() { super.onStop(); unbindService(mConnection); // Don't leak Services! } }
Golpear la tarjeta en vivo provoca un bloqueo inmediato y la salida Logcat anterior se descarga. Lo extraño es que si el openOptionsMenu () comentado es uncommented y el openOptionsMenu () existente es comentado, el primer toque realmente abrirá el menú. El segundo intento de abrir el menú fallará, con salida Logcat similar (la excepción BadTokenException es la excepción principal en lugar de una excepción interna en RuntimeException).
- Pantalla negra mostrada después de la intención de la cámara
- Google Play Services "No auténtico" en Glass
- Google Glass: seleccione "Cancelar" o "Aceptar" en el cuadro de diálogo
- ¿Cómo enviar su aplicación .apk Glass para que su cliente lo vea?
- ¿Cómo navegar por una aplicación de Google Glass GDK Immersion usando sólo comandos de voz?
- ¿Cómo podemos activar el modo de depuración en google glass para probar una aplicación android en google glass?
- ¿Es posible tener Android Voice Recognition (como un servicio personalizado) en Google Glass?
- Desbordamiento del búfer de Eclipse Google Glass
Como dijo Petey , pero necesita anular onAttachedToWindow()
además de onResume()
. Mi código ahora se parece a algo así:
private boolean isAttached = false; @Override public void onAttachedToWindow() { super.onAttachedToWindow(); this.isAttached = true; openOptionsMenu(); } @Override public void onResume() { super.onResume(); if (this.isAttached) openOptionsMenu(); }
Respuesta de dario en skylight1,
"Las muestras se han actualizado también – de Compass:"
@Override public void onAttachedToWindow() { super.onAttachedToWindow(); mAttachedToWindow = true; openOptionsMenu(); }
Ty dario
- Android Base64 codificar y decodificar return null en Unit Test
- Cocos2d-xv 2.0.4 FATAL EXCEPTION GLThread cuando se ejecuta en el emulador de Android