記一次懸浮窗的上線以及坑點總結

蘋果味的少年發表於2019-11-12

       剛剛發了一個大版本,終於有時間來總結一下技術點了,今天就來總結一下上個版本用到的懸浮窗吧,類似於微信公眾號文章裡的效果,可以拖動可以點選,拖動以後根據拖動的位置顯示不同的懸浮窗圖片,具備左右自動吸頂效果,當前app不在前臺時會自動隱藏。目前已經順利上線,很多問題已經解決,線上並沒有出現大的問題。今天我想把自己這次使用懸浮窗的方法以及其中的坑點告訴大家,希望能對大家有所幫助,碰到問題也可以一起討論,歡迎小夥伴們一起進步,come on快上車。

記一次懸浮窗的上線以及坑點總結

       最開始接到懸浮窗的需求也是有點虛的,因為之前沒有做過這方面,只是聽說過這個比較坑,等做完才發現這個是真的坑。廢話少說,直接進入主題,首先從我調研這個需求說起吧。遇到沒做過的需求,習慣性地去往github上搜尋懸浮窗相關的第三方庫,希望能站在巨人的肩膀上。不出所以然,我找到了一個第三方git庫:github.com/yhaolpz/Flo…,我下載了demo跑了一遍,發現還不錯,支援滑動事件和點選事件,可以設定大小,可以設定懸浮窗的顯隱。心裡樂滋滋地準備往自己專案裡搬運,可是剛想搬的時候發現就不對勁了,我們專案是在需要的時候再申請許可權,而不是開啟app就申請許可權並顯示。於是我一通亂改,由於對這一套不瞭解,改完發現越改越亂bug越多,心態已經崩了。這個時候突然靈光一閃,先去issue裡找找看有沒有人碰到同樣的問題,果然是船到橋頭自然直,還真讓我找到了。有個大神碰到相同的問題並且已經解決了,fork了程式碼並且進行了修改,這個git庫是:github.com/baneyue/Flo…,在此非常感謝前輩們的付出,站在巨人的肩膀上的感覺就是如此奇妙,自己也要多出一些技術部落格分享給別人,因為每個人做的技術需求都不大相同,應用場景不一樣也會導致遇到的Bug不經相同,要取長補短,哈哈。

       安卓的懸浮窗還是比較坑的,最坑的是許可權,其次沒有成熟的官方庫來做這個事情,導致很多開發者自己去做或多或少地都有不同的問題,再次懸浮窗是window,本身谷歌提供的api就很難使用,比如只能add不能replace,並且多次add還會導致crash,這種奇葩的為難開發者的設計真是毀三觀啊。吐槽完了活兒還是得自己幹,這裡先給大家介紹一個第三方懸浮窗程式碼的具體實現吧,程式碼也比較簡單,可能我一貼出來大家一下就能看懂了。

懸浮窗關鍵程式碼解讀

1.如何申請許可權

首先靜態申請一下

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />複製程式碼

然後為了相容高API,需要進行動態許可權申請,這裡需要根據API版本以及廠商進行區分了。如果版本大於26使用TYPE_APPLICATION_OVERLAY的方式即可,否則使用TYPE_PHONE。其次一般6.0以前是不需要動態申請懸浮窗許可權的,但小米是個特例,而且小米不同版本申請的方式可能還不一樣,在6.0以上申請方式和其他手機一樣,但是6.0以下就不同了

記一次懸浮窗的上線以及坑點總結記一次懸浮窗的上線以及坑點總結

小米6.0以下申請許可權

小米6.0以下申請許可權和小米MIUI版本有關,如下圖,需要對miui版本進行區分。具體的程式碼是我直接用的上面那個第三方庫裡的,有需要的可以去github上拉取直接用。

記一次懸浮窗的上線以及坑點總結

記一次懸浮窗的上線以及坑點總結

正常的申請流程,也就是6.0以上手機的申請流程是跳到一個自己封裝的空白許可權申請介面,然後跳轉到系統自帶的懸浮窗申請介面進行許可權申請,不管使用者給不給許可權,都關閉當前空頁面。由於系統不同,所以在不同手機上看到的許可權申請頁面顏值可能就會不一樣。

記一次懸浮窗的上線以及坑點總結

記一次懸浮窗的上線以及坑點總結

2.如何實現懸浮窗可隨手指拖動

思路非常簡單,監聽懸浮窗那個imageView的onTouchListener即可,在剛點選的ACTION_DOWN事件中記錄當前的x,y位置,然後在每次移動後獲取到本次移動的位置,二者相減就是需要移動的位置,這是自定義view的最基本操作了。

記一次懸浮窗的上線以及坑點總結

3.如何實現懸浮窗左右邊的吸頂效果

思路也非常簡單,監聽到手指抬起的動作後,判斷當前位置是靠近左邊還是右邊,靠近左邊就以位置動畫的方式平移到左邊,靠近右邊就平移到右邊。

記一次懸浮窗的上線以及坑點總結

4.核心,新增懸浮窗的方法

中規中矩,按照android官方的方式新增即可。即獲取系統的WindowManager,設定好引數,呼叫windowManager的addView()方法新增。需要注意的是,在隱藏懸浮窗的時候,最好是移除一下,下次需要顯示的時候再新增。

記一次懸浮窗的上線以及坑點總結記一次懸浮窗的上線以及坑點總結

記一次懸浮窗的上線以及坑點總結

坑點一覽,滿滿的乾貨

坑點一  多次新增view會導致crash

       這個是很多人都會碰到的,有時候沒辦法保證只新增一次,就算給了flag也會因為系統原因導致這個flag不準確。所以我在使用的時候就碰到了多次新增了的情況,最後的結果就是crash,解決方案就是在addView方法上加一個try...catch捕獲住該異常,妥妥的安全感。

記一次懸浮窗的上線以及坑點總結

坑點二  oppo r9(5.1.1)上是有許可權的,一直給返回沒許可權

       這段程式碼是處理6.0以下許可權申請的,上面紅色部分是原來的程式碼,下面綠色部分是改過之後的。由於oppo在6.0以下每次檢測懸浮窗許可權都給false,導致專案中每次都彈框讓使用者去給許可權,最後乾脆就oppo 6.0以下直接預設都有許可權好了。因為事實也如此,除了小米,其他品牌6.0以下都是有許可權的。

記一次懸浮窗的上線以及坑點總結

坑點三  如何處理輸入法和懸浮窗的層級關係

       微信裡的懸浮窗是在輸入法之下的,所以互動的同學也要求我們們的懸浮窗也要在輸入法之下。我檢視了一下WindowManager原始碼,我懸浮窗的優先順序TYPE_APPLICATION_OVERLAY,上面大字寫著明明是在輸入法之下的,但是實際表現是在輸入法之上了。

記一次懸浮窗的上線以及坑點總結

       這個問題也是折騰了半天,在原始碼裡找了半天找得是天昏地暗,終於功夫不負有心人,還真讓我找著了,windowManager是有一個flag專門用來設定懸浮窗和輸入法的關係的,但是由於之前沒設定,所以導致最後預設的輸入法還是在懸浮窗之下。

記一次懸浮窗的上線以及坑點總結

既然源頭找到了,設定起來就很簡單了,如下圖,設定好flag_alt_focusable_im這個flag即可。後面發現有一個帖子對flag總結得非常好,這裡也推薦給大家,有想了解其他flag的同學可以去看一下這篇帖子:blog.csdn.net/qq_33275597…

記一次懸浮窗的上線以及坑點總結

坑點四  如何處理退出app或者按home鍵的時候關閉懸浮窗

       這也是谷歌坑人的地方,都沒地方設定這個懸浮窗是否只用到app內,所以預設在桌面上也會顯示自己的懸浮窗。比如在微信裡顯示其他app的懸浮窗,這種糟糕的體驗可想而知,使用者不給你解除安裝就真是奇蹟了。為了解決這個問題,最初的實現方式是對所有經過的activity進行記錄,顯示就加1,頁面被掛起就減1,如果減到當前計數為0時說明所有頁面已經關閉了,就可以隱藏懸浮窗了。

記一次懸浮窗的上線以及坑點總結

但是實際上這麼做還是有問題的,在部分手機上如果是在首頁按返回鍵的話仍然不能隱藏,這個又是系統級的相容性問題。為了解決這問題,我後面又做了一個處理,通過註冊registerActivityLifecycleCallbacks監聽app的前後臺回撥,檢測到如果當前首頁被銷燬時,應該將懸浮窗進行隱藏。

記一次懸浮窗的上線以及坑點總結

坑點五  多次點選懸浮窗以後,開啟多個頁面

       如果你的懸浮窗點選事件是開啟頁面的話,這裡需要注意了,別忘了將這個開啟的頁面的啟動模式設定為singleTop或者是singleTask,從而複用同一個,遠離一直按返回的地獄操作。

android:launchMode="singleTop"複製程式碼

android:launchMode="singleTask"複製程式碼


總結:懸浮窗的坑,只有真正做過的人才懂,不過還好現在已經有很多現成的第三方程式碼可以CV。但是就算是CV了也不代表可以高枕無憂,應用場景不同總還是會遇到其他的問題,我這裡總結了自己遇到的部分坑點,以後還有的話會繼續更新,我相信踩的坑越多,成長才會越快。我也希望自己的工作經歷能夠幫助到大家少走彎路,就像我借鑑了前輩們的成果一樣。


demo地址:github.com/dongrong-fu…


相關文章