由於各個應用市場要求,需要在 2019年5月1日 之前把 target 升級到 26。所以對本公司全網的 App 和可能影響到的相關 SDK 做一個升級。本文主要記錄此次升級的過程和解決的一些問題。
其實升級 target 技術含量不是很高,但是因為涉及到庫(100 多個 SDK)和人員,依賴有點多,涉及到公司所有業務的 App, 而且改動的地方和細節也有多,很容易出現考慮不全,導致線上問題。
主要過程為以下幾步:
- 檢視官方文件
- 找下「輪子」,選擇一個合適自己的「輪子」, 優化「輪子」
- 修改對應的模組和 App,並接入到相關的業務方
- 遇到的問題,解決問題
檢視官方文件
因為公司的 App、元件、模組都是基於 target 22 和 support 24 進行開發的,所以要看下官方文件 releases/platforms 和 libraries/support-library 相關的文件,從中找到影響點。我們受影響主要有 2 個方面:
- 一、執行時許可權申請,(這個是大頭)
- 二、其他問題
找輪子
因為第一個許可權問題是比較普遍的,所以應該有相關的開源專案支援,為了效率,我們就不重複製造輪子。參考各個比較流行的開源方案,做了一下對比:
相關庫 | 需要修改 Activity 或者 Fragment | 設定介面跳轉 | 備註 | star 數 |
---|---|---|---|---|
AndPermission | 不需要 | 有, 可以參考 | 改動小 | 4911 |
Permissions4m | 需要 | 有,可以參考 | 改動多 | 1594 |
RxPermissions | 需要,不支援 Application 請求傳入請求 | 無,需要自己實現 | 改動多 | 7911 |
FcPermissions | 需要,需要繼承 FcPermissionsCallbacks | 無,需要自己實現 | 改動多 | 406 |
EasyPermission | 需要 | 無 | 改動多 | 7526 |
PermissionHelper | 需要繼承 BasePermissionActivity | 無 | 改動多 | 1191 |
通過以上對比,我們決定使用 AndPermission 的方案,因為這個對於我們現有 App 的侵入是最少的,改動點比較少,而且支援 Appliction 傳入(其實當使用 Application 傳入時候,會有問題,後面再說)。
說下 AndPermission
當時的考慮點是我們公司很多 SDK 設計的時候是沒有 Activity 的引用。但是我們的 SDK, 基本有個 Application 這個引用的,所以選擇了一個能夠支援傳入 Applicaiton 就能夠判斷許可權回撥的庫。為什麼 AndPermission 能夠支援呢?因為 AndPermission 在許可權校驗的時候會啟動 PermissionActivity,並且加上了 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
,所有的許可權請求都是在這個 PermissionActivity 處理,而且改動也比較少。
AndPermission 這個庫的動態許可權請求具體過程大夥可以看下 AndPermission 這個庫的原始碼。使用方式如下:
從 API 呼叫來看是十分簡單好用的。然後我又仔細看了一下它的實現過程,發現了有些不符合我們產品需求的地方:
- 業務方接入需要簡單,需要有一套預設的許可權申請失敗提示框
- 彈框提醒許可權必須是 Activity 的。但是 AndPermission 是支援 Application 傳入的,那麼就會有問題
- 比如我使用者點選某個按鈕導致後面的方法鏈中有可能連續呼叫多次的許可權請求,那麼就可能一次會彈出很多個框
這樣我們就需要在 AndPermission 基礎上進行改造,做出適合自己的動態許可權庫。我們自定義了預設的 onDenied (申請許可權失敗後的回撥)。 這樣業務方就不需要關心失敗後怎麼提示了,只要關心成功後把之前的業務邏輯放到成功後這裡執行就好了。許可權申請失敗提示框,用來啟動一個 Activity 來 show 這個 Dialog,這樣即使在前面傳入的是 Application 的 context 也是沒有問題了。對於多次請求許可權導致多次彈框的問題,我們在 AndPermission 的基礎上新增的請求佇列,只有上一個許可權請求處理完成後,才進行下一次許可權請求,這樣的話,即使使用者一次行為的方法鏈過程中有很多次請求也不會多次彈框。 然後當我用佇列管理請求許可權的時候,很怕某個許可權阻塞了,或者出了未知異常,所以我對每次許可權加入過程做了超時處理。以上幾點是對於自己業務場景的幾點考慮,進行的改造。
修改各個 SDK 和 App
U51AndPermission SDK 已經生成了,那接下來就是怎麼整合到各個 SDK 和 App 中了。首先我們要知道,我們的 App 哪些地方有可能呼叫到了需要許可權請求的 API。如果要人工去看效率實在是太低了。我們有 100 多個庫,不可能把庫看完且不出錯。所以我們使用了之前同事的一個外掛去掃描相關的 API,用來定位到可能出現許可權的類在哪裡,用的是哪個庫。這樣就提高了準確率和效率。
這個是我們外掛需要搜尋的 API
以下是部分搜尋結果
按照這種方法,我們本需要處理 100 多個類庫的,現在只有 20 個不到。一下子少了 4 倍的工作量,而且相對準確。
App 「必選許可權」方案選擇和問題處理
很多 App 啟動的時候需要一些必選許可權的,我們的 App 也是一樣的,需要在 App 啟動的時候驗證一個必選許可權。如果有必選許可權,那麼提示使用者授權,如果不授權,那麼就不能夠繼續使用我們的產品了。所以進入 App 主要功能前,需要一個前置的攔截。考慮過 2 種方案:
-
方案一、在 Applicaiton 的 onCreate 方法中去申請必選許可權,讓啟動 Activity 等待 Application 中許可權申請好了,再用訊息(EventBus) 通知 啟動 Activity 繼續走下去的流程
-
方案二、新建一個新的 啟動 Activity,在這個 Activity 中做申請許可權,申請完後再去啟動之前老的啟動 Activity
和對接的業務方討論,他們選擇了第一種方案, 這麼做的原因也是因為我們很多 SDK 的初始化程式碼在 Appliction 中,我們必須要在初始化 SDK 之前就應該把必要許可權拿到。如果選擇第二種,那麼我們的初始化程式碼需要移動到新的啟動 Activity 中,這樣改動風險有點高。
接入程式碼如下:
對接完後有幾個我們遇到的問題需要提下:
問題一、 因為在 App 啟動的時候,如果沒有必要許可權,那麼就會有彈框讓使用者設定許可權,這時候使用者點選 "設定",就會跳轉到 App 設定許可權頁面,當使用者授權回來的時候,有部分手機會出現黑屏。導致黑屏的原因是 U51Permission 傳入進去的 context 是 Application。如果是 Activity 就不會黑屏。所以解決方法是使用 Activity 去請求許可權,回撥方式是使用 Application.registerActivityLifecycleCallbacks
,如下:
問題二、 因為我們有些邏輯是放到前後臺切換的程式碼裡面的,前後切換的主要用主要用到 Application.registerActivityLifecycleCallbacks
這個回撥,根據 Activity 的生命週期統計來切換前後臺(前後臺的切換邏輯是使用統計 activity 的個數來實現的);所以 Application.registerActivityLifecycleCallbacks
這個操作應該是在 啟動 Activity 之前就應該被註冊。但是在申請必要許可權的時候,我先啟動 Activity 後再去註冊這個 callback 的,所以導致啟動 Activity 不在計算範圍內,導致前後臺的呼叫邏輯不準確,業務邏輯處理時機不對的問題。後來我們使用一個自己的 registerActivityLifecycleCallbacks
,命名 MyActivityLifecycleCallbacks
,在 Application 一開始啟動的時候就註冊了,然後把後面其他需要註冊的地方放到 MyActivityLifecycleCallbacks
中,由它統一通知其他需要前後臺的回撥。
替換之前
替換之後
可以看出來替換後改動程式碼很少,而且所有的前後臺切換都會統一到自己的 MyActivityLifecycleCallbacks
裡面進行集中管理。
其他問題
還有其他相關問題,網上都能夠找到,我就列舉一下,大家自己注意一下就好了:
- Android 7.0 相機相關問題
- 需要顯示的註冊廣播
- 黑白名單限制, debug 包會有彈框提示,release包是沒有的;但是這個需要注意,以後可能會有問題;也可以用掃描工具掃描一下這份名單
- 懸浮框實現的 LayoutParams.TYPE變動
android.os.FileUriExposedException
的異常,檔案共享的限制和第 1 條一樣Sevices.startService
有些手機會奔潰,這個我們的 App 之前就處理過了- vivo 手機相機許可權問題
- NotificationManager 應該使用 builder 去建立,需要有個 channel, Android 8.0 的修改
- 榮耀 8 手機,scrollView 巢狀 recycleView 顯示不全的問題
- Toast 相關的問題
總結
以上就是本次升級需要的修改點了,剩下的還有測試、灰度和正式上線。從整個適配過程來看,這個需求其實不是很難,但是從改動的點來說,溝通協調能力要求還是很高的,會涉及到大部分客戶端開發、測試和產品。
對於改造升級大範圍的基礎庫,每個環節儘量多思考和團隊成員多討論,切記不要一個人悶頭就是幹。但也不要過多的擔心,需要膽大心細,這樣才能把事情推進下去。好了,target 28 的適配也馬上要來了。
作者介紹
- Mr.Jie,51信用卡客戶端基礎組 Android 開發工程師,目前主要負責客戶端創新專案相關技術演進工作。