android的window一共分為三個(gè)級(jí)別,包括應(yīng)用級(jí)別的窗口,系統(tǒng)級(jí)別的窗口以及子窗口。其中dialog就屬于應(yīng)用級(jí)別的窗口。我們在使用dialog的時(shí)候,都知道dialog需要傳入activity作為構(gòu)建dialog的context。但是如果傳入getApplicationContext,就會(huì)報(bào)錯(cuò)Unable to add window -- token null is not for an application。那么是不是就只能傳入activity了呢?其實(shí)也不是。對話框拋出“無法添加窗口 – 令牌null不適用于應(yīng)用程序”與 getApplication()作為上下文這篇文章一共列出了十種解決方案,我們這里主要就是討論通過將應(yīng)用級(jí)別的dialog提升為系統(tǒng)級(jí)別的window,來解決傳入getApplicationContext()的問題。
一:通過window設(shè)置setType,提升window級(jí)別
系統(tǒng)window的級(jí)別從2000-2999不等
Dialog dialog=new Dialog(getApplicationContext());
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
TextView textView=new TextView(this);
textView.setText("Hello");
dialog.setContentView(textView);
dialog.show();
之后我們還需要在AndroidMenifest.xml中添加一個(gè)權(quán)限,不然就會(huì)出現(xiàn)下面這樣的異常。
Caused by: android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@d87e9e4 -- permission denied for this window type
需要添加的權(quán)限
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
至此我們在其他手機(jī)上,就都可以通過傳入getApplicationContext來解決彈出dialog的問題了,但是在小米手機(jī)上卻總是彈不出來。
二:小米手機(jī)彈出自定義系統(tǒng)window。
在小米手機(jī)上,我們需要手動(dòng)設(shè)置彈出系統(tǒng)window的權(quán)限,應(yīng)用才可以彈出系統(tǒng)級(jí)別的window,具體設(shè)置方法是設(shè)置-->授權(quán)管理-->應(yīng)用權(quán)限管理-->選擇自己的app-->勾選顯示懸浮窗
經(jīng)過上面的操作,設(shè)置懸浮窗權(quán)限成功,就可以正確彈出系統(tǒng)級(jí)window。
三:通過運(yùn)行時(shí)判斷是否是小米系統(tǒng)解決miui無法彈出系統(tǒng)對話框問題
除了通過上述方法提示用戶來設(shè)置系統(tǒng)級(jí)別window的權(quán)限問題,我們還可以通過代碼判斷是否是miui,這樣我們就單獨(dú)對miui做另外的處理。下面的方法,雖然沒有設(shè)置懸浮窗權(quán)限,但是依然能夠彈出系統(tǒng)級(jí)別的window。這是因?yàn)橥ㄟ^windownManager為dialog設(shè)置了setType。
public void get_dialog(View view){
Dialog dialog=new Dialog(getApplicationContext());
TextView textView=new TextView(this);
if(isMIUIRom()){
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_TOAST);
textView.setText("Hello xiaomi");
}else{
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
textView.setText("Hello not xiaomi");
}
dialog.setContentView(textView);
dialog.show();
}
public boolean isMIUIRom(){
String property =getSystemProperty("ro.miui.ui.version.name");
return !TextUtils.isEmpty(property);
}
public String getSystemProperty(String propName) {
String line;
BufferedReader input = null;
try {
Process p = Runtime.getRuntime().exec("getprop " + propName);
input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
line = input.readLine();
input.close();
} catch (IOException ex) {
return null;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return line;
}
四:應(yīng)用級(jí)別的dialog為什么離開acitivty的token就活不下去了?
詳情可以參考騰訊一位同學(xué)寫的博客淺析Android的窗口
。文章分析的很詳細(xì),這里就不重復(fù)了。簡要把這篇文章的大意記錄一下。
前面我們提到了窗口分為三種:
(1):應(yīng)用級(jí)別窗口,比如Activity。 Dialog 的窗口類型也是是 TYPE _ APPLICATION
(2):子窗口,比如PopupWindow。
(3):系統(tǒng)級(jí)別窗口,比如Toast。
其中應(yīng)用類型窗口,子窗口,以及部分系統(tǒng)窗口,都是需要token的,而其他一些系統(tǒng)窗口則可以不需要token。這里需要token的系統(tǒng)窗口類型如TYPE _ INPUT _ METHOD,TYPE _ VOICE _ INTERACTION,TYPE _ WALLPAPER,TYPE _ DREAM,TYPE _ ACCESSIBILITY _ OVERLAY。