使用onSaveInstanceState()处理设备旋转时,PopupWindow.showAtLocation()导致WindowManager $ BadTokenException

时间:2013-08-08 06:26:34

标签: android popupwindow screen-rotation

我一直在研究一个直接的文件管理器应用程序。我用于测试的设备是LG Nexus 4(v4.3)和Xperia x10i(v2.3.7)。 x10i的性能虽然有点迟缓,却没有问题。

轮换:

我跟踪了几个PopupWindows。我使用标志来确定当前哪个PopupWindow在屏幕上(目前,在任何给定时间,屏幕上最多只有一个PopupWindow)。在onSaveInstanceState(Bundle),中,我使用Bundle保存这些标记。在onCreate(Bundle)中,我检索这些标记并在onPostExecute()的{​​{1}}中使用它们。

问题:

如果在设备旋转时显示AsyncTask(called in onResume() and used for populating the ListView with data),则会销毁,重新创建活动,并再次显示PopupWindow。这适用于两种设备。但是,今天,当搜索弹出窗口显示时,我将PopupWindowx10i度旋转到90度。该应用程序因以下异常而崩溃:

270

第2852行:

08-08 01:55:51.961: E/AndroidRuntime(32373): FATAL EXCEPTION: main
08-08 01:55:51.961: E/AndroidRuntime(32373): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.view.ViewRoot.setView(ViewRoot.java:544)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:177)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:91)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.view.Window$LocalWindowManager.addView(Window.java:424)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.widget.PopupWindow.invokePopup(PopupWindow.java:907)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.widget.PopupWindow.showAtLocation(PopupWindow.java:767)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at com.apprehension.phylerfilemanager.Phyler.showPopupSearch(Phyler.java:2852)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at com.apprehension.phylerfilemanager.Phyler$DisplayFilesTask.onPostExecute(Phyler.java:3453)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at com.apprehension.phylerfilemanager.Phyler$DisplayFilesTask.onPostExecute(Phyler.java:1)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.os.AsyncTask.finish(AsyncTask.java:417)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.os.AsyncTask.access$300(AsyncTask.java:127)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.os.Handler.dispatchMessage(Handler.java:99)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.os.Looper.loop(Looper.java:123)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at android.app.ActivityThread.main(ActivityThread.java:3701)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at java.lang.reflect.Method.invokeNative(Native Method)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at java.lang.reflect.Method.invoke(Method.java:507)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:862)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
08-08 01:55:51.961: E/AndroidRuntime(32373):    at dalvik.system.NativeStart.main(Native Method)

如果我每90度旋转并暂停,问题就不存在了。当设备经过180度旋转而没有暂停时发生崩溃。

保存标志:

popupWindowSearch.showAtLocation(popupViewSearch, Gravity.CENTER, 0, 0);    

检索@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (searchPopup) { outState.putBoolean("searchPopup", searchPopup); outState.putString("searchKeyword", searchKeyword); outState.putInt("searchType", searchType); } if (....) { ........ ........ } } 中的标记:

onCreate(Bundle)

if (savedInstanceState != null) { rotated = true; if (savedInstanceState.containsKey("searchPopup")) { searchPopup = true; searchKeyword = savedInstanceState.getString("searchKeyword"); searchType = savedInstanceState.getInt("searchType"); } .... .... } 执行AsyncTask。在此onResume()的{​​{1}}中:

onPostExecute()

在Nexus 4上进行测试时不会抛出异常。我还尝试将AsyncTask发布到if (rotated) { rotated = false; if (searchPopup) { showPopupSearch(searchType, searchKeyword); // Line 3453 } .... .... } else { searchPopup = false; .... .... } 消息队列。问题依然存在。

我认为我处理屏幕旋转的方式存在问题。在我使用的应用程序中,屏幕旋转和布局更改顺利进行。在我的应用程序的情况下,您可以确切地告诉Runnable被解雇并重新创建。大多数应用使用mContentView's (the activity's main view)来处理屏幕旋转吗?我已经读过这种方法不正确。

2 个答案:

答案 0 :(得分:2)

最有可能发生的是x10i正在进行两次Activity实例化。这导致两个AsyncTasks运行。第一个将最终引用Activity的实例,在框架和窗口管理器的眼中,它不再存在(或应该存在),导致空令牌和结果异常。

在您的Activity#onStop中,您应该设置AsyncTask#cancel并在AsyncTask#onPostExecute中检查是否已取消,如果是,则不会创建弹出窗口。


实际解决方案:

Activity中创建一个在onCreate()中设置为false的标记。在onStop()中将其设置为true,然后在onPostExecute中检查是否已设置,如果是,则不显示弹出窗口。

答案 1 :(得分:0)

将PopupWindow显示为

final View parent = findViewById(R.id.{parentId});
parent.post(new Runnable() {
    @Override
    public void run() {
        mPopup.showAtLocation(parent, ...);
    }
});

解决了这个问题。