Android之Activity **** has leaked window android.widget.PopupWindow$PopupDecorView that was originall

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011068702/article/details/81370119

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方法里面

猜你喜欢

转载自blog.csdn.net/u011068702/article/details/81370119