先将错误帖上来:
E/AndroidRuntime(19905): FATAL EXCEPTION: main
E/AndroidRuntime(19905): android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?
E/AndroidRuntime(19905): at android.view.ViewRootImpl.setView(ViewRootImpl.java:670)
E/AndroidRuntime(19905): at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:248)
E/AndroidRuntime(19905): at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:69)
E/AndroidRuntime(19905): at com.android.camera.OnScreenHint.handleShow(OnScreenHint.java:178)
E/AndroidRuntime(19905): at com.android.camera.OnScreenHint.access$000(OnScreenHint.java:41)
E/AndroidRuntime(19905): at com.android.camera.OnScreenHint$1.run(OnScreenHint.java:197)
E/AndroidRuntime(19905): at android.os.Handler.handleCallback(Handler.java:800)
E/AndroidRuntime(19905): at android.os.Handler.dispatchMessage(Handler.java:100)
E/AndroidRuntime(19905): at android.os.Looper.loop(Looper.java:194)
E/AndroidRuntime(19905): at android.app.ActivityThread.main(ActivityThread.java:5407)
E/AndroidRuntime(19905): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(19905): at java.lang.reflect.Method.invoke(Method.java:525)
E/AndroidRuntime(19905): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
E/AndroidRuntime(19905): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
E/AndroidRuntime(19905): at dalvik.system.NativeStart.main(Native Method)
这个错误还是第一次碰到。onScreenHint 其实本质上是一个 popup 弹出框。那么这个弹出框是要依赖于一个父窗口或者父 View 的。
这个错误的根本原因是说父窗口还未创建完成的时候就调用了这个弹出框导致失败。
最后根据上述调用栈,顺滕摸瓜找到是在 ActivityBase.java 的 onResume() 函数中调用的。
源代码:
if (updateStorageHintOnResume()) {
showIfNeeded();
}
上述代码中 showIfNeed() 函数间接调用了 OnScreenHint.java 类的 handleShow() 函数
private synchronized void handleShow() {
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
final int gravity = mGravity;
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK)
== Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK)
== Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
if (mView.getParent() != null) {
mWM.removeView(mView);
}
mWM.addView(mView, mParams);//出问题的正是这一行
}
}
父View 没有创建好,咋办呢?这里有一个最简单的办法就是过一段时间再调这个函数,将函数 showIfNeed() 或者 此函数中的弹出框调用部分放到一个 Handler 中,并且通过延时来触发。
修改方法如下:
修改为:
if (updateStorageHintOnResume()) {
updateStorageSpace();
mHandler.sendEmptyMessageDelayed(UPDATE_STORAGE_HINT, 200);
}
Handler 中的代码:
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_STORAGE_HINT:
updateStorageHint();
return;
case CLEAR_SCREEN_DELAY: {
getWindow().clearFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
break;
}
case EXIT: {
finish();
break;
}
}
}
};
说明一下:上面的修改中将 showIfNeed() 函数一分为二,并且将间接调用 OnScreenHint.java handleShow() 的部分放到了一个 Handler 中来做,并且通过延时来触发。这样就不太可能出现秀弹出框的时候其父 View 还没有创建好的情况了。
网上有一篇类似的文章描述了popupWindow 中同样的错误,有需要可以参考:http://cb269267.iteye.com/blog/1787779