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