Android 8.0 运行时权限策略变化和适配方案
Android8.0也就是Android O即將要發布了,有很多新特性,目前我們可以通過AndroidStudio3.0 Canary版本下載Android O最新的系統映像的Developer Preview 4版本,Developer Preview 4是Android O正式版推出前的最后一個預覽版本,所以它是Android O的候選版本,我們可以使用它來完成開發和測試,讓我們的應用平穩過度到Android O。
后期會計劃出一篇Android O行為變化和兼容方案的文章,本篇文章主要講Android O行為變化的其中一點——系統運行時權限的策略變化和適配方案。
Android系統的運行時權限是從Android 6.0(Android M)開始加入的,如果你還不知道Android運行時權限,你可以看我在掘金的另一篇文章Android 6.0 運行時權限管理最佳實踐:
juejin.im/post/57d5de…
針對運行時權限管理,有很多開源的管理庫,去年這個時候本人也開源了一個運行權限管理方案,它最大程度上兼容了國產機,當然也兼容了Android 8.0:
github.com/yanzhenjie/…
在正式開始之前,先糾正一個問題,在網上看到有項目可以做到自定義申請授權的系統Dialog,首先要糾正就目前來看是絕對不行的,最多在調用申請的代碼之前彈一個自己的Dialog提示用戶要申請授權了。我快速拜讀了下那個項目源碼,果然如我想象的一樣,在繞了一個圈子后最終還是調用了系統申請授權的代碼。
Android O的運行時權限策略變化
如果你喜歡看Google官網的文章,你可以看這里:
developer.android.com/preview/beh…
在 Android O 之前,如果應用在運行時請求權限并且被授予該權限,系統會錯誤地將屬于同一權限組并且在清單中注冊的其他權限也一起授予應用。
對于針對Android O的應用,此行為已被糾正。系統只會授予應用明確請求的權限。然而一旦用戶為應用授予某個權限,則所有后續對該權限組中權限的請求都將被自動批準。
例如,假設某個應用在其清單中列出READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE。應用請求READ_EXTERNAL_STORAGE,并且用戶授予了該權限,如果該應用針對的是API級別24或更低級別,系統還會同時授予WRITE_EXTERNAL_STORAGE,因為該權限也屬于STORAGE權限組并且也在清單中注冊過。如果該應用針對的是Android O,則系統此時僅會授予READ_EXTERNAL_STORAGE,不過在該應用以后申請WRITE_EXTERNAL_STORAGE權限時,系統會立即授予該權限,而不會提示用戶。
下面我們還是以READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE為例來具體分析一下,這對我們現有的代碼有什么影響。
正式開始之前,我們先約定兩個方法:
/*** 拿到沒有被授權的權限。*/ getDeinedPermission(String... permissions); /*** 請求幾個權限。*/ requestPermission(String... deinedPermissions);復制代碼權限的常量在Manifest.permission類中,而READ_EXTERNAL_STORAGE權限是在API 16之后才添加的,所以在在Android M出來后為了適配更低版本的系統,我們一般是這樣申請權限的(偽代碼):
// 需要申請的權限。 String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_SMS,... };String[] deniedPermissions = getDeinedPermission(permissions);if(deniedPermissions.length <= 0) {// TODO do something... } else {requestPermission(deniedPermissions, callback); }復制代碼邏輯非常簡單清晰,其中的callback是申請權限的回調,這里我們申請了WRITE_EXTERNAL_STORAGE權限,在Android O之前,我們同時會得到READ_EXTERNAL_STORAGE權限,我們在其它地方涉及到讀取存儲卡的操作時只需要判斷有WRITE_EXTERNAL_STORAGE權限就去讀取了。
霸特,此時應用如果安裝在Android O的系統中我們會發現,判斷了有WRITE_EXTERNAL_STORAGE權限后去讀取存儲卡內容時應用崩潰了,原因就是我們沒有申請READ_EXTERNAL_STORAGE權限。
對Android O運行時權限策略變化的應對方案
針對Android O的運行時權限策略的特點,為了適配各個版本的系統,我們的代碼會變成如下方式(偽代碼):
// 需要申請的權限。 String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.READ_SMS,... };String[] deniedPermissions = getDeinedPermission(permissions);if(deniedPermissions.length <= 0) {// TODO do something... } else {requestPermission(deniedPermissions, callback); }復制代碼但是這樣會存在兩個問題,一是有的權限組權限比較多,開發者難易全部記住;二是READ_EXTERNAL_STORAGE這個權限常量是在API 16時才被添加到SDK中,類似這樣的權限常量還有好幾個,有的甚至在Android M時才被添加到SDK中。如果我們強制寫了,當APP運行在低版本的系統中時,還是會崩潰。有人就說了,我們在申請之前判斷系統版本不就好啦?當然,如果你不嫌麻煩,這是完全可以的。
升級方案
因此我們總結出一個更優的方案,歸根結底就是申請權限時要申請權限組,而不是單一的某個權限。所以我們按照系統權限組分類,把一個組的常量放到一個數組中,并根據系統版本為這個數組賦值,于是乎產生了這樣一個類:
public final class Permission {public static final String[] CALENDAR; // 讀寫日歷。public static final String[] CAMERA; // 相機。public static final String[] CONTACTS; // 讀寫聯系人。public static final String[] LOCATION; // 讀位置信息。public static final String[] MICROPHONE; // 使用麥克風。public static final String[] PHONE; // 讀電話狀態、打電話、讀寫電話記錄。public static final String[] SENSORS; // 傳感器。public static final String[] SMS; // 讀寫短信、收發短信。public static final String[] STORAGE; // 讀寫存儲卡。static {if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {CALENDAR = new String[]{};CAMERA = new String[]{};CONTACTS = new String[]{};LOCATION = new String[]{};MICROPHONE = new String[]{};PHONE = new String[]{};SENSORS = new String[]{};SMS = new String[]{};STORAGE = new String[]{};} else {CALENDAR = new String[]{Manifest.permission.READ_CALENDAR,Manifest.permission.WRITE_CALENDAR};CAMERA = new String[]{Manifest.permission.CAMERA};CONTACTS = new String[]{Manifest.permission.READ_CONTACTS,Manifest.permission.WRITE_CONTACTS,Manifest.permission.GET_ACCOUNTS};LOCATION = new String[]{Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION};MICROPHONE = new String[]{Manifest.permission.RECORD_AUDIO};PHONE = new String[]{Manifest.permission.READ_PHONE_STATE,Manifest.permission.CALL_PHONE,Manifest.permission.READ_CALL_LOG,Manifest.permission.WRITE_CALL_LOG,Manifest.permission.USE_SIP,Manifest.permission.PROCESS_OUTGOING_CALLS};SENSORS = new String[]{Manifest.permission.BODY_SENSORS};SMS = new String[]{Manifest.permission.SEND_SMS,Manifest.permission.RECEIVE_SMS,Manifest.permission.READ_SMS,Manifest.permission.RECEIVE_WAP_PUSH,Manifest.permission.RECEIVE_MMS};STORAGE = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE};}}}復制代碼在Android M以前使用某權限是不需要用戶授權的,只要在Manifest中注冊即可,在Android M之后需要注冊并申請用戶授權,所以我們根據系統版本在Android M以前用一個空數組作為權限組,在Android M以后用真實數組權限。
因為要傳入多個權限組,所以我們約定的兩個方法就不夠用了,所以我們加兩個方法:
/*** 拿到沒有被授權的權限。*/ String[] getDeinedPermission(String... permissions); /*** 請求幾個權限。*/ void requestPermission(String... deinedPermissions); /*** 拿到沒有被授權的權限。*/ String[] getDeinedPermission(String[]... permissions); /*** 請求幾個權限。*/ void requestPermission(String[]... deinedPermissions);復制代碼于是我們申請權限的代碼就簡化成這樣了:
// 這方法里面判斷版本,返回空數組或者沒有權限的數組。 String[] deniedPermissions = getDeinedPermission(Permission.STORAGE, Permission.SMS);if(deniedPermissions.length <= 0) {// TODO do something... } else {requestPermission(deniedPermissions, callback); }復制代碼當然這不是最簡化的,但是已經足以兼容到Android O的權限策略的變化了。
如果是AndPermission如何做到最簡
這里只是介紹下AndPermisison也兼容了Android O的權限變化,如果你覺得這個項目不適合你,你可以自行封裝一個,我比較鼓勵開發者自己動手,下面是開源地址:
github.com/yanzhenjie/…
它的一些簡單的特點:
申請多個權限組示例:
AndPermission.with(this).permission(Permission.CAMERA, Permission.SMS) // 多個權限組。.callback(new PermissionListener() {public void onSucceed(int i, @NonNull List<String> list) {// TODO do something...}public void onFailed(int i, @NonNull List<String> list) {// TODO 用戶沒有同意授權,一般彈出Dialog讓用戶去Setting中授權。}}).start();復制代碼申請單個或者某幾個權限示例,因為Android O的出現,現在不鼓勵這樣使用了,但是在Android O正式發布前沒有問題:
AndPermission.with(this).permission(// 多個不同權限組權限,現在不鼓勵這樣使用了,但是在Android O正式發布前沒有問題。Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_SMS) .callback(new PermissionListener() {public void onSucceed(int i, @NonNull List<String> list) {// TODO do something...}public void onFailed(int i, @NonNull List<String> list) {// TODO 用戶沒有同意授權,一般彈出Dialog讓用戶去Setting中授權。}}).start();復制代碼關于Android O的運行時權限策略變化和應對方案的介紹到這里就結束了,如果還不理解的可以在文章下方留言。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android 8.0 运行时权限策略变化和适配方案的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 多个线程直接按对数据进行操作容易引发线程
- 下一篇: Android反编工具的使用-Andro