1 问题
在页面实现了popupWindows,旋转屏幕的时候提示下面的错误
Activity **** has leaked window android.widget.PopupWindow$PopupDecorView that was originally added here
很明显,窗口内存泄漏,因为旋转屏幕的时候,依次会调用activity的onPause方法onStop方法onDestroy方法,当依附的activity销毁时,但是这个时候popupWindows句柄还在就会有这个问题
2 初步测试
在onDestroy方法里面关闭popupWindows
@Override
protected void onDestroy() {
super.onDestroy();
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
popupWindow = null;
}
}
然后测试依然还是出现上面的错误,这不日了狗吗?
3 分析
Activity组件的应用程序窗口视图对象和ViewRoot对象关联通过窗口管理器(WindowManager)关联
1) 分析Window类getWindowManager方法
public WindowManager getWindowManager() {
return mWindowManager;
}
2) 分析WindowManagerImpl类
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Display mDisplay;
3) 分析WindowManagerGlobal.java类
public void setStoppedState(IBinder token, boolean stopped) {
synchronized (mLock) {
int count = mViews.size();
for (int i = 0; i < count; i++) {
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
root.setWindowStopped(stopped);
}
}
}
}
4) 分析ViewRootImple类的setWindowStopped方法
void setWindowStopped(boolean stopped) {
if (mStopped != stopped) {
mStopped = stopped;
if (!mStopped) {
scheduleTraversals();
}
}
}
然后看mStopped变量意思
// Set to true if the owner of this window is in the stopped state,
// so the window should no longer be active.
boolean mStopped = false;
如果mStoped是true,说明这个windows不再活跃,也就是死了,我们看到
if (mStopped != stopped) {
mStopped = stopped;
if (!mStopped) {
scheduleTraversals();
}
}
如果stoped等于true的话,mStopped初始化是false,这个时候进去判断,然后mStopped值为true,这个时候窗口就死了,那么window就是死了,其它的试图(popUpWindow)都依赖这个window,如果这个时候window死了,其它的试图(popUpWindow)不死,那么就会有这个异常,那么我们找到哪里调用了这个函数setWindowStopped函数,
5) 在Activity.java类里面调用如下
final void performStop() {
mDoReportFullyDrawn = false;
mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
if (!mStopped) {
if (mWindow != null) {
mWindow.closeAllPanels();
}
if (mToken != null && mParent == null) {
WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
也就是说调用performStop方法调用了setWindowStopped(mToken, true)方法,刚才分析如果传递true进去,就说就把windows搞死了,那么如果在这个之后再去关闭popUpwindow还是会出问题,我们必须在这个方法调用之前关闭掉popUpwindow,因为这个时候windows还没有死,
我们知道
Instrumentation.java里面有callActivityOnStop方法
会调到这里来,然后在Activity调用onStop方法就会调到这里来,所以我们需要在Activity的onStop方法之前关闭popupwindow
所以需要加在onPause方法里面
4 最后解决
在onPause关闭视图
@Override
protected void onPause() {
super.onPause();
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
popupWindow = null;
}
}
5 总结
在Activity里面的试图,对话框,PopupWindow我们都需要放在onPause里面关闭,而不是在onDestroy方法里面