前言
搞項目的時候遇到一個Bug纳本,當(dāng)ListPopupWindow顯示的時候开镣,按返回鍵就會報標(biāo)題類似的錯誤。但是在點(diǎn)擊Toolbar的NavigationIcon時(設(shè)置了finish的點(diǎn)擊事件)郊艘,卻不會報這個窗體溢出的Bug谐鼎。
Why
首先來了解下這個錯誤產(chǎn)生的原因(呃,網(wǎng)上說的很清楚了段只,我就直接復(fù)制了):
Android的每一個Activity都有個WindowManager窗體管理器腮猖,同樣,構(gòu)建在某個Activity之上的對話框赞枕、PopupWindow也有相應(yīng)的WindowManager窗體管理器澈缺。但是它們不能脫離Activity而單獨(dú)存在著,因為需要Activity的Context炕婶,所以當(dāng)某個Dialog或者某個PopupWindow正在顯示的時候我們干掉了承載該Dialog(或PopupWindow)的Activity姐赡,就會拋WindowLeaked異常了,因為這個Dialog(或PopupWindow)的WindowManager已經(jīng)沒有誰可以附屬了(Context)柠掂。
怎么解決
既然問題原因知道了项滑,好的,我解決起來不是很簡單么涯贞,在Activity的OnDestroy方法里枪狂,如果popupWindow顯示的話,我讓它dismiss掉不就好了宋渔。嗯州疾,就是這樣,簡單皇拣!
@Override
protected void onDestroy() {
super.onDestroy();
if(mMerchantPopupWindow!=null&&mMerchantPopupWindow.isShowing())
mMerchantPopupWindow.dismiss();
mMerchantPopupWindow=null;
}
But,我被打臉了孝治,還是報這個異常。
那么好审磁,我在onBackPressed方法中讓popup消失總可以了吧,然而岂座,還是不行态蒂,還是被打臉了。
How to 辦?
怒费什,仔細(xì)分析下〖鼗郑現(xiàn)身吧手素,柯南。
1.為什么點(diǎn)擊toolbar的icon時瘩蚪,不報這個錯泉懦,同樣是finish掉這個頁面
搞起,搞起疹瘦,搞事情崩哩,簡直!
唔言沐,原來ListPopupWindow里面默認(rèn)是設(shè)置PopupWindow點(diǎn)擊外面PopupWindow消失的邓嘹。
mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch&& !mDropDownAlwaysVisible);
而這兩個變量默認(rèn)是false:
private booleanmDropDownAlwaysVisible=false;
private booleanmForceIgnoreOutsideTouch=false;
原來如此,點(diǎn)擊icon的時候险胰,popup先dismiss了汹押,然后頁面finish掉不會報錯。
2.為什么我設(shè)置了按返回鍵后起便,在頁面銷毀時讓popup消失棚贾,卻不行呢?
再次搞事情榆综。既然將一個Activity組件的應(yīng)用程序窗口視圖對象與一個ViewRoot對象關(guān)聯(lián)是通過該Activity組件所使用的窗口管理器(WindowManager)來執(zhí)行的妙痹。調(diào)用所獲得的本地窗口管理器wm(類型為LocalWindowManager)的成員函數(shù)addView來執(zhí)行關(guān)聯(lián)應(yīng)用程序窗口視圖對象和ViewRoot對象的操作。
那么我們就來找一找WindowManager奖年。
找到了细诸!
mWindowManager = mWindow.getWindowManager();
嗯,來看下Window類:
public WindowManager getWindowManager() {
return mWindowManager;
}
mWindowManager=((WindowManagerImpl)wm).createLocalWindowManager(this);
嗯陋守,再來找下WindowManagerImpl:
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobalmGlobal=WindowManagerGlobal.getInstance();
public WindowManagerImplcreateLocalWindowManager(WindowparentWindow){
return new WindowManagerImpl(mDisplay,parentWindow);
}
@Override
public void addView(Viewview,ViewGroup.LayoutParamsparams){
mGlobal.addView(view,params,mDisplay,mParentWindow);
}
嗯震贵,看起來WindowManagerGlobal的嫌疑很大啊,小伙子水评,搞事情靶上怠!WindowManagerGlobal#performStop
final void performStop() {
....
if(!mStopped) {
...
if(mToken!=null&&mParent==null) {
WindowManagerGlobal.getInstance().setStoppedState(mToken,true);
}
...
mInstrumentation.callActivityOnStop(this);
...
}
}
那么WindowManagerGlobal的setStoppedState又是啥呢中燥?看這里寇甸。
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);
}
}
}
}
再來看下ViewRootImpl的setWindowStoped方法:
void setWindowStopped(boolean stopped) {
if (mStopped != stopped) {
mStopped = stopped;
if (!mStopped) {
scheduleTraversals();
}
}
}
ViewRootImpl#mStopped 當(dāng)Window狀態(tài)為stoped時為true,代表Window不再活躍疗涉。
// 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;
那么Activity#performStop方法啥時候調(diào)用的呢拿霉。再來看下前面的WindowManagerGlobal#performStop方法。
看到這句mInstrumentation.callActivityOnStop(this)沒咱扣?
來看下Instrumentation#callActivityOnStop方法
public void callActivityOnStop(Activity activity) {
activity.onStop();
}
也就是說绽淘,執(zhí)行完Activity#performStop方法會調(diào)用Activity#OnStop方法。在OnStop之前闹伪,performStop()就執(zhí)行了沪铭。
完結(jié)撒花
那么在onStop之前壮池,onPause時候,讓popupWindow沒消失的消失好啦杀怠!以前沒仔細(xì)注意這個問題椰憋,尷尬!