1.關閉通知權限時不顯示華為等手機的Toast
2.不同手機上吐司排隊機制可能不同;
3.Toast的BadTokenException問題;
當發現系統Toast有問題時,很多同學使用自定義TYPE_TOAST子彈框來達到同樣的效果。雖然在所有情況下效果都還可以,但TYPE_TOAST還是有問題:
4.token null在Android 8.0之後無效(部分機型實測);
5.Android 7.1以後,不允許同時顯示兩個TYPE_TOAST彈出框(部分型號問題實測);
所以解決方案是:
我相信很多同學老項目裏打包的ToastUtil直接用ApplicationContext作為上下文,然後在需要彈出窗口的時候直接用ToastUtil.show(str),這是我們使用起來最方便的方式。
當然,妳仍然可以用YToast使用這種打包方式,但是這種方式可能無法在下面的場景中成功顯示彈出窗口(原生Toast在這個場景中也無法彈出),但是請放心,不會導致應用崩潰,出現這個場景的概率很小,具備以下三個必要條件:
1.通知欄權限關閉(默認情況下,通知欄權限全部打開)。
2.非MIUI手機
3.3以上的部分手機。Android8.0(我最近測試的8.0+設備都沒有這個問題)。
但是如果想保證彈出窗口在所有場景下都能正常顯示,還是建議在YToast.make(context)的時候傳入Activity作為上下文,這樣YToast就會啟用ActivityToast在這個場景下顯示彈出窗口。
接下來詳細分析上面提到的五個問題。
看下面Toast源代碼中的show()方法,通過AIDL獲取Inovation Manager,將下壹個顯示過程的控制權交給INotificationManager服務。將在NMS中驗證Toast,當無法驗證通知權限時,將不顯示Toast。
當然,NMS在不同的rom中可能是不同的。比如MIUI就修改了這部分內容,所以小米手機關閉的通知權限不會導致Toast無法顯示。
如何解決這個問題?只要能繞過NotificationManagerService。
YToast通過使用TYPE_Toast實現全局彈出功能,不使用系統TOAST或NMS服務,因此不受通知權限的限制。
我找到了四個設備,創建了兩個不同重力的Toast並調用了show()方法。因此,有四種顯示效果:
出現這個問題的原因應該是NMS在各大廠商ROM中維護Toast隊列的邏輯不同。
同樣,YToast也維護自己的隊列邏輯,以保證在所有手機上使用DToast的效果是壹樣的。
當YToast中連續出現多個彈出窗口時:
優先級相同時,前壹個終止,後壹個直接顯示;
優先級不同,如果後壹個優先級更高,前壹個會終止,後壹個直接顯示。
windowToken在什麽情況下會失敗?
UI線程被阻塞,導致TN.show()沒有及時執行。當NotificationManager的檢測超時時,WMS中的令牌將被刪除,從而使令牌失效。
怎麽解決?
因此,我們需要對8.0之前的用戶進行同樣的操作。YToast通過反射完成此操作,如下所示:
Android8.0以後,WindowManager受到了限制和修改,特別是對於TYPE_TOAST的窗口,必須傳遞壹個令牌進行驗證。
API 25:(PhoneWindowManager.java源代碼)
API 26:(PhoneWindowManager.java源代碼)
為了解決第壹個問題,DovaToast不得不選擇繞過NotificationManagerService的控制,但是由於windowToken是由NMS生成的,繞過NMS無法獲得有效的windowToken,所以DovaToast作為TYPE_TOAST可能會陷入第四個問題。
所以DToast選擇在DovaToast出現這個問題的時候引入ActivityToast,在DovaToast無法正常顯示的時候創建壹個附加在Activity上的彈出窗口來顯示,但是ActivityToast只會顯示當前的活動,沒有跨頁功能。
如果有更好的解決方案,那壹定是獲取浮窗權限,然後切換到TYPE_PHONE,但是浮窗權限往往不容易獲取。目前恐怕除了微信,其他app都無法保證用戶的浮窗權限。
YToast的彈出策略是壹次只顯示壹個彈出窗口,邏輯上避免了這個問題。因此,只有這個異常被捕獲。
其他建議
如果可以接受Toast不跨接口,建議使用SnackBar。