Android添加懸浮窗的一些總結(jié)

首先先了解幾個(gè)知識(shí)點(diǎn):

SDK版本低于7.1.1使用WindowManager.LayoutParams.TYPE_TOAST是不需要授權(quán)的,可以像平時(shí)用的toast一樣展示與任何界面之上,而除了TYPE_TOAST之外都需要申請(qǐng)懸浮窗的權(quán)限:

懸浮窗授權(quán)

若用戶(hù)并沒(méi)有申請(qǐng)此權(quán)限而添加懸浮窗的話(huà)會(huì)有以下crash信息:

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)

所以使用TYPE_TOAST的好處很明顯,但是由于從7.1.1開(kāi)始很多人會(huì)遇到以下crash信息:

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限制了使用TYPE_TOAST,查看7.1.1 版本代碼的變更,我們可以看到有這樣的一條記錄:

提交記錄

Prevent apps to overlay other apps via toast windows 這句話(huà)已經(jīng)說(shuō)明了一切,就是試圖改變TYPE_TOAST之前被濫用的現(xiàn)狀。

所以7.1.1之后不要再使用TYPE_TOAST,改為使用TYPE_PHONE,并且需要在開(kāi)啟懸浮窗前必須主動(dòng)申請(qǐng)一下權(quán)限:

      try {
            Class clazz = Settings.class;
            Field field = clazz.getDeclaredField(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);

            Intent intent = new Intent(field.get(null).toString());
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.setData(Uri.parse("package:" + context.getPackageName()));
            context.startActivity(intent);
        } catch (Exception e) {
            GLog.e(TAG, e.getMessage());
        }

所以我們的WindowManager初始化改為了:

        WindowManager.LayoutParams wmParams = new WindowManager.LayoutParams();

        windowManager = (WindowManager) context.getSystemService(context.WINDOW_SERVICE);
        if (Build.VERSION.SDK_INT > 24) {
            wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
        } else {
            wmParams.type = WindowManager.LayoutParams.TYPE_TOAST;
        }

        wmParams.format = PixelFormat.RGBA_8888;
        //設(shè)置浮動(dòng)窗口不可聚焦(實(shí)現(xiàn)操作除浮動(dòng)窗口外的其他可見(jiàn)窗口的操作)
        wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        //調(diào)整懸浮窗顯示的??课恢脼樽髠?cè)置頂
        wmParams.gravity = Gravity.LEFT | Gravity.TOP;
        // 以屏幕左上角為原點(diǎn),設(shè)置x、y初始值,相對(duì)于gravity
        wmParams.x = 0;
        wmParams.y = 0;

        //設(shè)置懸浮窗口長(zhǎng)寬數(shù)據(jù)
        wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容