WindowManager 懸浮窗大家應該經常用了,在這咱不說他的具體實現,也不談他的源碼分析。
主要探討權限適配,層級介紹,還有可能的Bug。
一、層級
懸浮層本質上是將view送給WindowManager去玩,它將view設置不同類型,也就有了不同的層級(z軸)展示,不一樣的價錢不一樣的服務。
總體來說有應用窗口(APPLICATION_WINDOW)、子窗口(SUB_WINDOW)、系統窗口(SYSTEM_WINDOW)三種類型,應用窗口z軸範圍是1~99,子窗口的範圍是1001~1999,系統窗口是(2000~2999)。
來做個全套:
看起來一目瞭然啊,我當初以爲拿到這個我就可以實現很多解釋不清楚的效果,能上天了,但是android再一次證明它可以 啪啪 打你臉,讓你叫爸爸。
比如:權限問題,手機android版本不一樣,有的Type類型你就用不了。不帶懸念的,即便你申請了懸浮權限也不給你。
二、系統版本7.1.1以上
除了TYPE_TOAST之外都需要申請懸浮窗的權限,否則出現:
android.view.WindowManager$BadTokenException: Unable to add window android.view.ViewRootImpl$W@8d2124d -- permission denied for this window type
at android.app.ActivityThread.handleCreateService(ActivityThread.java:2887)
at android.app.ActivityThread.-wrap4(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1427)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
以爲申請權限就了事了? 不可能,還有:
android.view.WindowManager$BadTokenException: Unable to add window -- window android.view.ViewRootImpl$W@363f7b1 has already been added
at android.view.ViewRootImpl.setView(ViewRootImpl.java:691)
at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:342)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
at android.widget.Toast$TN.handleShow(Toast.java:434)
at android.widget.Toast$TN$2.handleMessage(Toast.java:345)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Android官方版本升級對這塊權限做了不小的調整,google對其進行了管控,防止濫用應用懸浮窗造成各種干擾,影響體驗。
所以解決問題需要
1.申請懸浮窗權限,或者使用層級較低的TYPE_PHONE懸浮窗。
if (Build.VERSION.SDK_INT > 24) {
wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
}
2. 系統8.0以上使用SYSTEM_ALERT_WINDOW 權限的應用無法再使用以下窗口類型來在其他應用和系統窗口上方顯示提醒窗口:
- ·TYPE_PHONE
- ·TYPE_PRIORITY_PHONE
- ·TYPE_SYSTEM_ALERT
- ·TYPE_SYSTEM_OVERLAY
- ·TYPE_SYSTEM_ERROR
相反,應用必須使用名爲 TYPE_APPLICATION_OVERLAY 的新窗口類型。
3. 將build.gradle 的sdk版本不超過7.0
4. 想到更好的實現方式,之後找產品或者設計懟回去