在使用dialog或者popupwindow時經常會出現以下錯誤導致應用crash,即獲取的token爲空,原因是使用的控件需要綁定Activity的context,而控件生成時Activity還沒有完成加載,因此會出現以下錯誤。
產生錯誤的情景一般有兩種:
1. 在Activity還沒有完成加載時,調用了popupwindow或需要綁定Activity的控件,例如在onCreate或onResume中設置了popupwindow.showAtLocation方法;
2. 在Activity destroy後調用了上述類型的控件,例如在銷燬Activity時調用了Dialog.show()。
對於第一種情況有兩種解決方法:
1. 通過handler延遲加載:
private Handler popupHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
popupWindow.showAtLocation(rootView, Gravity.CENTER,0, 0);
popupWindow.update();
break;
}
}
};
2. 使用onWindowFocusChanged(boolean hasFocus),個人比較推薦這種方式,該方法的含義我們可以從源碼註釋裏看到:
Called when the current {@link Window} of the activity gains or loses
* focus. This is the best indicator of whether this activity is visible
* to the user. As a general rule, however, a resumed activity will have window focus...
即該方法是Activity對用戶可見時最好的指示,並且一個resumed Activity總是有獲取到焦點,也就是說當hasFocus爲true時,Activity是肯定加載完成了的,此時再調用需要綁定Activity的控件就不會出現token is null 這種錯誤了。
/**
* Monitoring activity has finished.If it has finished, it's focus will change and call this method.
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (hasFocus) {
mPopupText.setText((mSeekBarDef.getProgress()-off+1)+"分鐘");
View rootView = LayoutInflater.from(MainActivity.this).inflate(R.layout.activity_main,null);
mPopupWindow.showAtLocation(rootView, Gravity.NO_GRAVITY,x,y);
}
}
對於第二種情景的解決方法是使用Activity.isFinishing()方法,還是從源碼入手:
/**
* Check to see whether this activity is in the process of finishing,
* either because you called {@link #finish} on it or someone else
* has requested that it finished. This is often used in
* {@link #onPause} to determine whether the activity is simply pausing or
* completely finishing.
*
* @return If the activity is finishing, returns true; else returns false.
*
* @see #finish
*/
public boolean isFinishing() {
return mFinished;
}
那麼該方法的作用就是判斷是否是否使用了finish而使Activity處於finishing的進程之中:
if (!MainActivity.this.isFinishing()){
alertDialog.show();
}