android – 在XE16中调用openOptionsMenu()会导致WindowManager.BadTokenException

前端之家收集整理的这篇文章主要介绍了android – 在XE16中调用openOptionsMenu()会导致WindowManager.BadTokenException前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个GDE应用程序在XE12中运行良好但现在在转换到GDK之后在XE16中崩溃:19.特别是,在Activity中调用openOptionsMenu()(在这种情况下,打开Live Card上的选项菜单)会导致BadTokenExceptions.

Logcat输出

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

服务中将活动绑定到Live Card的方法

@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,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.
    }

而有问题的活动:

/**
 * 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!
    }
}

点击Live Card会导致立即崩溃,并且上面的Logcat输出被转储.奇怪的是,如果注释掉的openOptionsMenu()被取消注释并且现有的openOptionsMenu()被注释掉,则第一次点击将实际打开菜单.打开菜单的第二次尝试将失败,具有类似的Logcat输出(BadTokenException是主要异常,而不是RuntimeException上的内部异常).

解决方法

As petey said,但除了onResume()之外,还需要覆盖onAttachedToWindow().我的代码现在看起来像:
private boolean isAttached = false;

@Override
public void onAttachedToWindow() {
  super.onAttachedToWindow();
  this.isAttached = true;
  openOptionsMenu();
}

@Override
public void onResume() {
  super.onResume();
  if (this.isAttached)
    openOptionsMenu();
}

猜你在找的Android相关文章