優(yōu)雅地申請(qǐng)運(yùn)行時(shí)權(quán)限

前言

從android 6.0(API 級(jí)別 23)開(kāi)始景东,android引入了運(yùn)行時(shí)權(quán)限,用戶開(kāi)始在應(yīng)用運(yùn)行時(shí)向其授予權(quán)限奔誓,而不是在應(yīng)用安裝時(shí)向其授予權(quán)限耐薯,如果應(yīng)用的某項(xiàng)功能需要使用到受運(yùn)行時(shí)權(quán)限保護(hù)的資源(例如相機(jī)、位置丝里、麥克風(fēng)等)曲初,但在運(yùn)行該功能前沒(méi)有動(dòng)態(tài)地申請(qǐng)相應(yīng)的權(quán)限,那么在調(diào)用該功能時(shí)就會(huì)拋出SecurityException異常杯聚, android 6.0已經(jīng)推出了很多年了臼婆,相信大家對(duì)于運(yùn)行時(shí)權(quán)限的申請(qǐng)過(guò)程已經(jīng)非常的熟悉,但是android的運(yùn)行時(shí)權(quán)限的申請(qǐng)過(guò)程一直都是非常的繁瑣的幌绍,主要有兩步:

1颁褂、在需要申請(qǐng)權(quán)限的地方檢查該權(quán)限是否被同意故响,如果同意了就直接執(zhí)行,如果不同意就動(dòng)態(tài)申請(qǐng)權(quán)限颁独;

2彩届、重寫Activity或Fragment的onRequestPermissionsResult方法,在里面根據(jù)grantResults數(shù)組判斷權(quán)限是否被同意誓酒,如果同意就直接執(zhí)行樟蠕,如果不同意就要進(jìn)行相應(yīng)的提示,如果用戶勾選了“don't ask again”靠柑,還要引導(dǎo)用戶去“Settings”界面打開(kāi)權(quán)限寨辩,這時(shí)還要重寫onActivityResult判斷權(quán)限是否被同意.

就是這簡(jiǎn)單的兩步,卻夾雜了大量的if else語(yǔ)句歼冰,不但不優(yōu)雅靡狞,而且每次都要寫重復(fù)的樣板代碼,可能android的開(kāi)發(fā)者也意識(shí)到了這一點(diǎn)隔嫡,在最新androidx中引入了activity result api甸怕,通過(guò)activity result api你可以不需要自己管理requestCode,只需要提供需要請(qǐng)求的權(quán)限和處理結(jié)果的回調(diào)就行腮恩,讓權(quán)限請(qǐng)求簡(jiǎn)單了一點(diǎn)蕾各,但是如果在權(quán)限請(qǐng)求的過(guò)程中,用戶點(diǎn)擊拒絕或者拒絕并不再詢問(wèn)庆揪,那么我們還是需要自己處理這些情況,但是這些處理流程都是一樣的妨托,完全可以封裝起來(lái)缸榛,所以我就把以前的一個(gè)使用無(wú)界面fragment代理權(quán)限申請(qǐng)的庫(kù)重構(gòu)了一下,讓權(quán)限的請(qǐng)求流程更加簡(jiǎn)單兰伤,本文會(huì)先復(fù)習(xí)一下權(quán)限的分類内颗,然后再介紹PermissionHelper申請(qǐng)權(quán)限時(shí)的設(shè)計(jì),最后記錄一下從android 6.0后隨著系統(tǒng)的迭代跟權(quán)限申請(qǐng)相關(guān)的重要行為變更敦腔。

權(quán)限的分類

android中所有的預(yù)定義權(quán)限(不包括廠商自定義的)都可以在Manifest.permission這個(gè)靜態(tài)類中找到定義均澳,android把權(quán)限分為四類:普通權(quán)限、簽名權(quán)限符衔、危險(xiǎn)權(quán)限和特殊權(quán)限找前,每一種類型的權(quán)限都分配一個(gè)對(duì)應(yīng)的Protection Level,分別為:normal判族、signature躺盛、dangerous和appop,下面簡(jiǎn)單介紹一下這四種類型的權(quán)限

1形帮、普通權(quán)限

普通權(quán)限也叫正常權(quán)限槽惫,Protection Level為normal周叮,它不需要?jiǎng)討B(tài)申請(qǐng),你只需要在AndroidManifest.xml中靜態(tài)地聲明界斜,然后系統(tǒng)在應(yīng)用安裝時(shí)就會(huì)自動(dòng)的授予該應(yīng)用相應(yīng)的權(quán)限仿耽,當(dāng)應(yīng)用獲得授權(quán)時(shí),它就可以訪問(wèn)應(yīng)用沙盒外受該普通權(quán)限保護(hù)地?cái)?shù)據(jù)或操作各薇,這些數(shù)據(jù)或操作不會(huì)泄漏或篡改用戶的隱私项贺,對(duì)用戶或其他應(yīng)用幾乎沒(méi)有風(fēng)險(xiǎn)。

2得糜、簽名權(quán)限

這類權(quán)限我們用得比較少敬扛,它只對(duì)擁有相同簽名的應(yīng)用開(kāi)放,Protection Level為signature朝抖,它也不需要?jiǎng)討B(tài)申請(qǐng)啥箭,例如應(yīng)用A在AndroidManifest.xml中自定義了一個(gè)permission且在權(quán)限標(biāo)簽中加入android:protectionLevel=”signature”,表示應(yīng)用A聲明了一個(gè)簽名權(quán)限治宣,那么應(yīng)用B想要訪問(wèn)應(yīng)用A受該權(quán)限保護(hù)的數(shù)據(jù)時(shí)急侥,必須要在AndroidManifest.xml中聲明該權(quán)限,同時(shí)要用與應(yīng)用A相同的簽名打包侮邀,這樣系統(tǒng)在應(yīng)用B安裝時(shí)才會(huì)自動(dòng)地授予應(yīng)用B該權(quán)限坏怪,應(yīng)用B在獲得授權(quán)后就可以訪問(wèn)該權(quán)限控制的數(shù)據(jù),其他應(yīng)用即使知道這個(gè)權(quán)限绊茧,也在AndroidManifest.xml中聲明了該權(quán)限铝宵,但由于應(yīng)用簽名不同,安裝時(shí)系統(tǒng)不會(huì)授予它該權(quán)限华畏,這樣其他應(yīng)用就無(wú)法訪問(wèn)受該權(quán)限保護(hù)的數(shù)據(jù)鹏秋。

還有一些簽名權(quán)限不會(huì)供第三方應(yīng)用程序使用,只會(huì)供系統(tǒng)預(yù)裝應(yīng)用使用亡笑,這種簽名權(quán)限的Protection Level為signature和privileged侣夷。

3、危險(xiǎn)權(quán)限

危險(xiǎn)權(quán)限也叫運(yùn)行時(shí)權(quán)限仑乌,Protection Level為dangerous百拓,跟普通權(quán)限相反,一旦應(yīng)用獲取了該類權(quán)限晰甚,用戶的隱私數(shù)據(jù)就會(huì)面臨被泄露或篡改的風(fēng)險(xiǎn)衙传,所以如果你想使用該權(quán)限保護(hù)的數(shù)據(jù)或操作,就必須在AndroidManifest.xml中靜態(tài)地聲明需要用到的危險(xiǎn)權(quán)限厕九,并在訪問(wèn)這些數(shù)據(jù)或操作前動(dòng)態(tài)的申請(qǐng)權(quán)限粪牲,系統(tǒng)就會(huì)彈出一個(gè)權(quán)限請(qǐng)求彈窗征求用戶的同意,除非用戶同意該權(quán)限止剖,否則你不能使用該權(quán)限保護(hù)的數(shù)據(jù)或操作腺阳。

所有的危險(xiǎn)權(quán)限都有對(duì)應(yīng)的權(quán)限組落君,android預(yù)定義了11個(gè)權(quán)限組(根據(jù)android 11總結(jié)),這11個(gè)權(quán)限組中包含了30個(gè)危險(xiǎn)權(quán)限和幾個(gè)普通權(quán)限亭引,當(dāng)我們動(dòng)態(tài)的申請(qǐng)某個(gè)危險(xiǎn)權(quán)限時(shí)绎速,都是按權(quán)限組申請(qǐng)的,當(dāng)用戶一旦同意授權(quán)該危險(xiǎn)權(quán)限焙蚓,那么該權(quán)限所對(duì)應(yīng)的權(quán)限組中的其他在AndroidManifest.xml中注冊(cè)的權(quán)限也會(huì)同時(shí)被授權(quán)纹冤,android預(yù)定義的11個(gè)權(quán)限組包含的危險(xiǎn)權(quán)限如下:

Permission Group Dangerous Permissions
CALENDAR (日歷) READ_CALENDAR
WRITE_CALENDAR
CALL_LOG (通話記錄,Added in android 29) READ_CALL_LOG
WRITE_CALL_LOG
PROCESS_OUTGOING_CALLS
CAMERA (相機(jī)) CAMERA
CONTACTS (通訊錄) READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION (位置信息) ACCESS_COARSE_LOCATION
ACCESS_FINE_LOCATION
ACCESS_BACKGROUND_LOCATION (Added in android 10)
MICROPHONE (麥克風(fēng)) RECORD_AUDIO
PHONE (電話) READ_PHONE_NUMBERS
READ_PHONE_STATE
CALL_PHONE
ANSWER_PHONE_CALLS
ADD_VOICEMAIL
USE_SIP
ACCEPT_HANDOVER (Added in android 9)
SENSORS (身體傳感器) BODY_SENSORS
SMS (短信) READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_SMS
RECEIVE_MMS
SEND_SMS
STORAGE (存儲(chǔ)空間) READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE
ACCESS_MEDIA_LOCATION (Added in android 10)
ACTIVITY_RECOGNITION (身體活動(dòng)购公,Added in android 10) ACTIVITY_RECOGNITION (Added in android 10)

4萌京、特殊權(quán)限

特殊權(quán)限用于保護(hù)一些特定的應(yīng)用程序操作,Protection Level為appop宏浩,使用前也需要在AndroidManifest.xml中靜態(tài)地聲明知残,也需要動(dòng)態(tài)的申請(qǐng),但是它不同于危險(xiǎn)權(quán)限的申請(qǐng)比庄,危險(xiǎn)權(quán)限的申請(qǐng)會(huì)彈出一個(gè)對(duì)話框詢問(wèn)你是否同意求妹,而特殊權(quán)限的申請(qǐng)需要跳轉(zhuǎn)到指定的設(shè)置界面,讓你手動(dòng)點(diǎn)擊toggle按鈕確認(rèn)是否同意佳窑,截止到android 11制恍,我了解到的常用的5個(gè)特殊權(quán)限為:

  • SYSTEM_ALERT_WINDOW:允許應(yīng)用在其他應(yīng)用的頂部繪制懸浮窗,當(dāng)你創(chuàng)建的懸浮窗是TYPE_APPLICATION_OVERLAY類型時(shí)需要申請(qǐng)這個(gè)權(quán)限神凑;
  • WRITE_SETTINGS:允許應(yīng)用修改系統(tǒng)設(shè)置净神,當(dāng)你需要修改系統(tǒng)參數(shù)Settings.System時(shí)需要申請(qǐng)?jiān)摍?quán)限,例如修改系統(tǒng)屏幕亮度等溉委;
  • REQUEST_INSTALL_PACKAGES: 允許應(yīng)用安裝未知來(lái)源應(yīng)用鹃唯,android 8.0以后當(dāng)你在應(yīng)用中安裝第三方應(yīng)用時(shí)需要申請(qǐng)這個(gè)權(quán)限,否則不會(huì)跳轉(zhuǎn)到安裝界面薛躬;
  • PACKAGE_USAGE_STATS:允許應(yīng)用收集其他應(yīng)用的使用信息,當(dāng)你使用UsageStatsManager相關(guān)Api獲取其他應(yīng)用的信息時(shí)需要申請(qǐng)這個(gè)權(quán)限呆细;
  • MANAGE_EXTERNAL_STORAGE(Added in android 11):允許應(yīng)用訪問(wèn)作用域存儲(chǔ)(scoped storage)中的外部存儲(chǔ)型宝,android 11以后強(qiáng)制新安裝的應(yīng)用使用作用域存儲(chǔ),但是對(duì)于文件管理器這一類的應(yīng)用它們需要管理整個(gè)SD卡上的文件絮爷,所以針對(duì)這些特殊應(yīng)用可以申請(qǐng)這個(gè)權(quán)限來(lái)獲得對(duì)整個(gè)SD卡的讀寫權(quán)限趴酣,當(dāng)應(yīng)用授予這個(gè)權(quán)限后,它就可以訪問(wèn)文件的真實(shí)路徑坑夯,注意這個(gè)權(quán)限是很危險(xiǎn)的岖寞,聲明這個(gè)權(quán)限上架應(yīng)用時(shí)可能需要進(jìn)行審核.

除了特殊權(quán)限,LOCATION權(quán)限組中的位置權(quán)限也有點(diǎn)特殊柜蜈,需要注意一下仗谆,位置信息的獲取不僅依賴位置權(quán)限的動(dòng)態(tài)申請(qǐng)還依賴系統(tǒng)定位開(kāi)關(guān)指巡,如果你沒(méi)有打開(kāi)定位開(kāi)關(guān)就申請(qǐng)了位置權(quán)限,那么就算用戶同意授權(quán)位置權(quán)限隶垮,應(yīng)用通過(guò)Location相關(guān)Api也無(wú)法獲取到位置信息藻雪,所以申請(qǐng)位置權(quán)限前,最好先通過(guò)LocationManager#isProviderEnabled方法判斷是否打開(kāi)定位開(kāi)關(guān)后再進(jìn)行位置權(quán)限的申請(qǐng)狸吞,如果沒(méi)有打開(kāi)定位開(kāi)關(guān)需要先跳轉(zhuǎn)到設(shè)置界面打開(kāi)定位開(kāi)關(guān)勉耀,偽代碼如下:

val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) or locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    //請(qǐng)求位置權(quán)限
} else {
    //跳轉(zhuǎn)到開(kāi)啟定位的地方
    Toast.makeText(this, "檢測(cè)到未開(kāi)啟定位服務(wù),請(qǐng)開(kāi)啟", Toast.LENGTH_SHORT).show()
    val intent = Intent().apply {
        action = Settings.ACTION_LOCATION_SOURCE_SETTINGS
    }
    startActivityForResult(intent, REQUEST_CODE_LOCATION_PROVIDER)
}

當(dāng)然,上面危險(xiǎn)權(quán)限和特殊權(quán)限的判斷與申請(qǐng)蹋偏,PermissionHelper都已經(jīng)替你做好了封裝便斥,你只需要像平常一樣在AndroidManifest.xml中靜態(tài)地聲明權(quán)限,然后在代碼中動(dòng)態(tài)地申請(qǐng)就行威始,下面我把危險(xiǎn)權(quán)限和特殊權(quán)限都統(tǒng)稱為動(dòng)態(tài)權(quán)限枢纠,因?yàn)樗鼈兌际切枰獎(jiǎng)討B(tài)申請(qǐng)的。

動(dòng)態(tài)權(quán)限申請(qǐng)?jiān)O(shè)計(jì)

動(dòng)態(tài)權(quán)限的申請(qǐng)依據(jù)不同的android版本和應(yīng)用targetSdkVersion有著不同的行為字逗,主要有兩種處理京郑,如下:

  • android版本 <= 5.1 或者 應(yīng)用的targetSdkVersion <= 22:當(dāng)用戶同意安裝應(yīng)用時(shí),系統(tǒng)會(huì)要求用戶授權(quán)應(yīng)用聲明的所有權(quán)限葫掉,包括動(dòng)態(tài)權(quán)限些举,如果用戶不同意授權(quán),只能拒絕安裝應(yīng)用俭厚,如果用戶同意全部授權(quán)户魏,他們撤銷權(quán)限的唯一方式就是卸載應(yīng)用;
  • android版本 >= 6.0 且 應(yīng)用的targetSdkVersion >= 23:當(dāng)用戶同意安裝應(yīng)用時(shí)挪挤,系統(tǒng)不再?gòu)?qiáng)制用戶必須授權(quán)動(dòng)態(tài)權(quán)限叼丑,系統(tǒng)只會(huì)授權(quán)應(yīng)用除動(dòng)態(tài)權(quán)限之外的普通權(quán)限,而動(dòng)態(tài)權(quán)限需要應(yīng)用使用到相關(guān)功能時(shí)才動(dòng)態(tài)申請(qǐng)扛门,當(dāng)申請(qǐng)動(dòng)態(tài)權(quán)限時(shí)鸠信,用戶可以選擇授權(quán)或拒絕每項(xiàng)權(quán)限,即使用戶同意授權(quán)權(quán)限论寨,用戶也可以隨時(shí)進(jìn)入應(yīng)用的“Settings”中調(diào)整應(yīng)用的動(dòng)態(tài)權(quán)限授權(quán)星立,所以你每次使用到該權(quán)限的功能時(shí),都要?jiǎng)討B(tài)申請(qǐng),因?yàn)橛脩粲锌赡茉凇癝ettings”界面中把它再次關(guān)閉掉.

在android版本 <= 5.1 或者 應(yīng)用的targetSdkVersion <= 22時(shí),系統(tǒng)使用的是AppOps來(lái)進(jìn)行權(quán)限管理嘶朱,這是android在4.4推出的一套應(yīng)用程序操作權(quán)限管理咧七,AppOps所管理的是所有可能涉及用戶隱私和安全的操作,例如access notification、keep weak lock、display toast 等等吟逝,而運(yùn)行時(shí)權(quán)限管理是android 6.0才出現(xiàn)占业,是基于AppOps的實(shí)現(xiàn)绒怨,進(jìn)一步做了動(dòng)態(tài)請(qǐng)求封裝和明確的規(guī)范,同時(shí)當(dāng)targetSdkVersion <= 22的應(yīng)用運(yùn)行在 >= 6.0的android系統(tǒng)上時(shí)纺酸,動(dòng)態(tài)權(quán)限可以在“Settings”界面中關(guān)閉窖逗,應(yīng)用運(yùn)行過(guò)程中使用到相關(guān)功能時(shí)就會(huì)由于沒(méi)有權(quán)限而出現(xiàn)崩潰,這時(shí)只能使用AppOps的 checkOp方法來(lái)檢測(cè)對(duì)應(yīng)的權(quán)限是否已經(jīng)授權(quán)餐蔬,沒(méi)有權(quán)限就跳轉(zhuǎn)到“Settings”界面碎紊,考慮到目前android 6.0已經(jīng)推出了很久,應(yīng)用商店也不允許targetSdkVersion < 23的應(yīng)用上架樊诺,所以為了減少框架的復(fù)雜度仗考,動(dòng)態(tài)權(quán)限申請(qǐng)?jiān)O(shè)計(jì)就沒(méi)有考慮兼容AppOps的權(quán)限管理操作,所以當(dāng)你使用PermissionHelper時(shí)應(yīng)用的targetSdkVersion要 >= 23词爬。

PermissionHelper支持危險(xiǎn)權(quán)限和特殊權(quán)限的申請(qǐng)秃嗜,只需要一行代碼就可以發(fā)起權(quán)限請(qǐng)求,具有生命周期感應(yīng)能力顿膨,只在界面可見(jiàn)時(shí)才發(fā)起請(qǐng)求和回調(diào)結(jié)果锅锨,同時(shí)當(dāng)系統(tǒng)配置更改例如屏幕旋轉(zhuǎn)后能夠恢復(fù)之前權(quán)限申請(qǐng)流程,不會(huì)中斷權(quán)限申請(qǐng)流程恋沃,靈活性高必搞,可以設(shè)置請(qǐng)求前、拒絕后回調(diào)囊咏,在回調(diào)發(fā)生時(shí)暫停權(quán)限申請(qǐng)流程恕洲,然后根據(jù)用戶意愿再?zèng)Q定是否繼續(xù)權(quán)限申請(qǐng)流程,整個(gè)申請(qǐng)過(guò)程如圖:

PermissionHelper可以通過(guò)設(shè)置回調(diào)在權(quán)限申請(qǐng)開(kāi)始前和權(quán)限被拒絕后把要請(qǐng)求的權(quán)限和被拒絕的權(quán)限回調(diào)出去梅割,在回調(diào)中你可以通過(guò)彈窗向用戶解釋要申請(qǐng)的權(quán)限對(duì)應(yīng)用的必要性霜第,引導(dǎo)用戶繼續(xù)授權(quán)或再次授權(quán),PermissionHelper不定制彈窗UI户辞,彈窗的UI由開(kāi)發(fā)者自定義泌类,開(kāi)發(fā)者只需要在用戶同意或拒絕后調(diào)用回調(diào)中的Process實(shí)例的相應(yīng)方法就能讓被暫停的權(quán)限申請(qǐng)流程恢復(fù),然后在最終的結(jié)果回調(diào)中處理結(jié)果就行底燎,整個(gè)過(guò)程都是鏈?zhǔn)降娜姓ィP(guān)于向用戶解釋權(quán)限申請(qǐng)?jiān)虻膹棿埃瑥棿皟?nèi)容建議包含下面的3點(diǎn):

1书蚪、包含需要授權(quán)的權(quán)限列表的描述喇澡;

2迅栅、包含確認(rèn)按鈕殊校,用戶可以點(diǎn)擊確認(rèn)按鈕再次授權(quán)或跳轉(zhuǎn)到”Settings“;

3读存、包含取消按鈕为流,用戶可以點(diǎn)擊取消按鈕放棄授權(quán).

如果用戶不授權(quán)這個(gè)權(quán)限呕屎,就會(huì)導(dǎo)致應(yīng)用無(wú)法繼續(xù)運(yùn)行下去,可以考慮取消第3步的取消按鈕敬察,即無(wú)法取消這個(gè)彈窗秀睛,一定要用戶再次授權(quán)或跳轉(zhuǎn)到”Settings“去授權(quán)。

PermissionHelper整個(gè)框架的設(shè)計(jì)參考了okhttp的攔截器模式莲祸,通過(guò)責(zé)任鏈模式的形式把危險(xiǎn)權(quán)限申請(qǐng)蹂安、特殊權(quán)限申請(qǐng)、申請(qǐng)前處理和申請(qǐng)后處理劃分為一個(gè)個(gè)節(jié)點(diǎn)锐帜,然后通過(guò)Chain串聯(lián)起各個(gè)節(jié)點(diǎn)田盈,每個(gè)節(jié)點(diǎn)只負(fù)責(zé)對(duì)應(yīng)的內(nèi)容,如下:

val originalRequest = Request()    
val interceptors = listOf(
    StartRequestNode(),
    RequestLocationNode(),
    RequestNormalNode(),
    RequestSpecialNode(),
    PostRequestNode(),
    FinishRequestNode()
)
DefaultChain(originalRequest, interceptors).process(originalRequest)

通過(guò)這樣的形式PermissionHelper就可以很靈活的控制權(quán)限申請(qǐng)流程缴阎,對(duì)于生命周期感應(yīng)能力的實(shí)現(xiàn)PermissionHelper使用了Lifecycle+LiveData組件允瞧,這兩個(gè)都是官方支持的用于實(shí)現(xiàn)需要響應(yīng)生命周期感應(yīng)的操作,可以編寫更輕量級(jí)和更易于維護(hù)的代碼蛮拔,避免界面銷毀后的內(nèi)存泄漏述暂,對(duì)于系統(tǒng)配置更改后的數(shù)據(jù)恢復(fù)則使用到了ViewModel組件,這是官方支持的用于保存需要在配置更改后恢復(fù)的數(shù)據(jù)建炫,例如一些UI相關(guān)的數(shù)據(jù)畦韭,通過(guò)這三件套 + 責(zé)任鏈模式實(shí)現(xiàn)了一個(gè)簡(jiǎn)單易用的權(quán)限申請(qǐng)框架,更多詳細(xì)使用和實(shí)現(xiàn)細(xì)節(jié)可以查看代碼倉(cāng)庫(kù)踱卵。

權(quán)限申請(qǐng)相關(guān)變更

自android 6.0推出動(dòng)態(tài)權(quán)限申請(qǐng)之后廊驼,有一些申請(qǐng)行為也隨著系統(tǒng)的迭代發(fā)生變化,目的都是更好的保護(hù)用戶的隱私權(quán)惋砂,使得權(quán)限申請(qǐng)對(duì)用戶感知:

android 8.0以后并且應(yīng)用的targetSdkVersion >= 28時(shí)妒挎,應(yīng)用申請(qǐng)某個(gè)危險(xiǎn)權(quán)限授權(quán),用戶同意后西饵,系統(tǒng)不再錯(cuò)誤地把該危險(xiǎn)權(quán)限對(duì)應(yīng)的權(quán)限組中的其他在AndroidManifest.xml中注冊(cè)的權(quán)限一并授予給應(yīng)用酝掩,系統(tǒng)只會(huì)授予應(yīng)用明確請(qǐng)求的權(quán)限,然而眷柔,一旦用戶應(yīng)用同意授權(quán)某個(gè)危險(xiǎn)權(quán)限期虾,則后續(xù)對(duì)該危險(xiǎn)權(quán)限的權(quán)限組中的其他權(quán)限請(qǐng)求都會(huì)被自動(dòng)批準(zhǔn),而不會(huì)提示用戶驯嘱,例如某個(gè)應(yīng)用在AndroidManifest.xml中注冊(cè)READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE權(quán)限镶苞,應(yīng)用申請(qǐng)READ_EXTERNAL_STORAGE權(quán)限并且用戶同意,在android 8.0之前鞠评,系統(tǒng)在用戶同意后還會(huì)一并授予WRITE_EXTERNAL_STORAGE權(quán)限茂蚓,因?yàn)樗蚏EAD_EXTERNAL_STORAGE權(quán)限是同一個(gè)權(quán)限組并且也在AndroidManifest.xml中注冊(cè),但在android 8.0之后并且應(yīng)用的targetSdkVersion >= 28,系統(tǒng)在用戶同意后只會(huì)授予READ_EXTERNAL_STORAGE權(quán)限聋涨,但是如果后來(lái)應(yīng)用又申請(qǐng)WRITE_EXTERNAL_STORAGE權(quán)限晾浴,系統(tǒng)會(huì)立即授予該權(quán)限,而不會(huì)提示用戶牍白,換句話說(shuō)脊凰,如果只申請(qǐng)了外部存儲(chǔ)空間讀取權(quán)限,在低版本下(android < 8.0)對(duì)外部存儲(chǔ)空間使用寫入操作是沒(méi)有問(wèn)題的茂腥,但是在高版本(android >= 8.0 && targetSdkVersion >= 28)下是會(huì)出現(xiàn)問(wèn)題的狸涌,解決方案是將兩個(gè)讀和寫的權(quán)限一起申請(qǐng)。

android 9.0增加了CALL_LOG(通話記錄)權(quán)限組最岗,并把READ_CALL_LOG杈抢、WRITE_CALL_LOG]、PROCESS_OUTGOING_CALLS權(quán)限從PHONE(電話)權(quán)限組移動(dòng)到了CALL_LOG權(quán)限組仑性,CALL_LOG權(quán)限組使得用戶能夠更好地控制需要訪問(wèn)電話通話記錄敏感信息的應(yīng)用程序惶楼,例如讀取通話記錄和識(shí)別電話號(hào)碼。

android 10引入了很多隱私變更诊杆,新增了ACTIVITY_RECOGNITION(身體活動(dòng))權(quán)限和權(quán)限組歼捐,允許應(yīng)用檢測(cè)用戶的步數(shù)或分類用戶的身體活動(dòng)如步行、騎自行車等晨汹;同時(shí)android 10引入了作用域存儲(chǔ)豹储,當(dāng)應(yīng)用啟用作用域存儲(chǔ)時(shí),WRITE_EXTERNAL_STORAGE權(quán)限會(huì)失效淘这,應(yīng)用對(duì)WRITE_EXTERNAL_STORAGE權(quán)限的申請(qǐng)不會(huì)對(duì)應(yīng)用的存儲(chǔ)訪問(wèn)權(quán)限產(chǎn)生任何影響剥扣,并且WRITE_EXTERNAL_STORAGE會(huì)在未來(lái)被廢棄,因?yàn)樽饔糜虼鎯?chǔ)的目的就是不讓應(yīng)用隨意的修改應(yīng)用沙盒外的外部存儲(chǔ)铝穷;同時(shí)新增了ACCESS_BACKGROUND_LOCATION權(quán)限钠怯,歸屬于LOCATION權(quán)限組,用于后臺(tái)運(yùn)行的應(yīng)用訪問(wèn)用戶定位時(shí)申請(qǐng)曙聂,與ACCESS_FINE_LOCATION和ACCESS_COARSE_LOCATION這些前臺(tái)定位權(quán)限區(qū)分開(kāi)晦炊,當(dāng)你的應(yīng)用targetSdkVersion >= 29并且運(yùn)行在android 10以上時(shí),應(yīng)用在后臺(tái)訪問(wèn)定位時(shí)需要?jiǎng)討B(tài)的申請(qǐng)后臺(tái)定位權(quán)限宁脊,當(dāng)你把后臺(tái)定位權(quán)限和前臺(tái)定位權(quán)限一起申請(qǐng)時(shí)断国,彈窗授權(quán)框會(huì)有2個(gè)允許選項(xiàng):始終允許僅在應(yīng)用使用過(guò)程中允許,點(diǎn)擊始終允許表示同時(shí)授權(quán)后臺(tái)定位權(quán)限和前臺(tái)定位權(quán)限榆苞,點(diǎn)擊僅在應(yīng)用使用過(guò)程中允許表示僅授權(quán)前臺(tái)定位權(quán)限稳衬,然后下次再次申請(qǐng)時(shí)只會(huì)單獨(dú)申請(qǐng)后臺(tái)定位權(quán)限,并且也會(huì)有2個(gè)允許選項(xiàng)坐漏,并且要點(diǎn)擊始終允許才會(huì)讓后臺(tái)定位權(quán)限申請(qǐng)通過(guò)薄疚,當(dāng)你的應(yīng)用targetSdkVersion < 29運(yùn)行在android 10以上時(shí)弄砍,應(yīng)用在申請(qǐng)前臺(tái)定位權(quán)限時(shí)系統(tǒng)會(huì)把后臺(tái)定位權(quán)限一并授予給應(yīng)用;android 10還新增了ACCESS_MEDIA_LOCATION權(quán)限输涕,歸屬于STORAGE (存儲(chǔ)空間) 權(quán)限組,android 10以后慨畸,因?yàn)殡[私問(wèn)題莱坎,默認(rèn)不再提供圖片的地理位置信息,要獲取該信息需要向用戶申請(qǐng)ACCESS_MEDIA_LOCATION權(quán)限寸士,并使用MediaStore.setRequireOriginal()接口更新文件Uri檐什。

android 11也引入了很多隱私變更,android 11強(qiáng)制新安裝的應(yīng)用(targetSdkVersion >= 30)啟用作用域存儲(chǔ)弱卡,新增MANAGE_EXTERNAL_STORAGE用于代替WRITE_EXTERNAL_STORAGE權(quán)限乃正,提供給手機(jī)管家、文件管理器這類需要管理整個(gè)SD卡上的文件的應(yīng)用申請(qǐng)婶博;android 11中當(dāng)用戶開(kāi)啟“安裝未知來(lái)源應(yīng)用”權(quán)限后返回應(yīng)用瓮具,應(yīng)用會(huì)被殺死重啟,該行為與強(qiáng)制分區(qū)存儲(chǔ)有關(guān)凡人;從android 11后名党,如果應(yīng)用對(duì)某個(gè)權(quán)限連續(xù)點(diǎn)擊多次拒絕,那么下一次請(qǐng)求該權(quán)限時(shí)系統(tǒng)會(huì)直接拒絕連授權(quán)彈窗都不會(huì)彈出挠轴,該行為等同于android 11之前勾選了don‘t ask again传睹;android 11后還新增了一次性權(quán)限(One-time permissions)和權(quán)限自動(dòng)重置功能(Permissions auto-reset),這些變更只要你正確的進(jìn)行運(yùn)行時(shí)權(quán)限請(qǐng)求就不需要做額外適配岸晦;同時(shí)android 11后當(dāng)targetSdkVersion < 30的應(yīng)用把后臺(tái)定位權(quán)限和前臺(tái)定位權(quán)限一起申請(qǐng)時(shí)欧啤,彈窗授權(quán)框的允許選項(xiàng)中不再會(huì)顯示始終允許選項(xiàng),只有本次允許僅在應(yīng)用使用過(guò)程中允許启上,也就說(shuō)點(diǎn)擊允許時(shí)只會(huì)授予你前臺(tái)定位權(quán)限不再默認(rèn)授予你后臺(tái)定位權(quán)限邢隧,而android 11后targetSdkVersion >= 30的應(yīng)用的ACCESS_BACKGROUND_LOCATION權(quán)限需要獨(dú)立申請(qǐng),不能與前臺(tái)權(quán)限一起申請(qǐng)冈在,如果與前臺(tái)權(quán)限一起申請(qǐng)府框,系統(tǒng)會(huì)直接拒絕連授權(quán)彈窗都不會(huì)彈出,系統(tǒng)推薦增量請(qǐng)求權(quán)限讥邻,這樣對(duì)用戶更友好迫靖,同時(shí)用戶必須先同意前臺(tái)權(quán)限后才能進(jìn)入后臺(tái)定位權(quán)限的申請(qǐng)。

可以看到從android 10引入ACCESS_BACKGROUND_LOCATION權(quán)限以來(lái)兴使,后臺(tái)定位權(quán)限的申請(qǐng)一直都非常特殊系宜,它在android 10可以和前臺(tái)定位權(quán)限一起申請(qǐng),而在android 11又不可以一起申請(qǐng)還有先后申請(qǐng)順序发魄,針對(duì)這種特殊情況盹牧,申請(qǐng)后臺(tái)定位權(quán)限時(shí)要做到:

  • 1俩垃、先請(qǐng)求前臺(tái)定位權(quán)限,再請(qǐng)求后臺(tái)定位權(quán)限汰寓;
  • 2口柳、單獨(dú)請(qǐng)求后臺(tái)定位權(quán)限,不要與其他權(quán)限一同請(qǐng)求.

上面這些PermissionHelper都已經(jīng)做好了處理有滑,申請(qǐng)時(shí)只需要把后臺(tái)定位權(quán)限和前臺(tái)定位權(quán)限一起傳進(jìn)去就行跃闹。

結(jié)語(yǔ)

本文主要讓讓大家對(duì)權(quán)限的申請(qǐng)流程有進(jìn)一步的認(rèn)識(shí),然后可以通過(guò)對(duì)動(dòng)態(tài)權(quán)限的封裝毛好,將檢測(cè)動(dòng)態(tài)權(quán)限望艺,請(qǐng)求動(dòng)態(tài)權(quán)限,權(quán)限設(shè)置跳轉(zhuǎn)肌访,監(jiān)聽(tīng)權(quán)限設(shè)置結(jié)果等處理和業(yè)務(wù)功能隔離開(kāi)來(lái)找默,業(yè)務(wù)以后可以非常快速的接入動(dòng)態(tài)權(quán)限支持吼驶,提高開(kāi)發(fā)效率惩激。

以上就是本文的全部?jī)?nèi)容!

參考資料:

Request app permissions

安卓系統(tǒng)權(quán)限蟹演,你真的了解嗎咧欣?

Android 6.0 運(yùn)行權(quán)限解析

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市轨帜,隨后出現(xiàn)的幾起案子魄咕,更是在濱河造成了極大的恐慌,老刑警劉巖蚌父,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哮兰,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡苟弛,警方通過(guò)查閱死者的電腦和手機(jī)喝滞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)膏秫,“玉大人右遭,你說(shuō)我怎么就攤上這事$拖鳎” “怎么了窘哈?”我有些...
    開(kāi)封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)亭敢。 經(jīng)常有香客問(wèn)我滚婉,道長(zhǎng),這世上最難降的妖魔是什么帅刀? 我笑而不...
    開(kāi)封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任让腹,我火速辦了婚禮远剩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骇窍。我一直安慰自己瓜晤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布腹纳。 她就那樣靜靜地躺著痢掠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪只估。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天着绷,我揣著相機(jī)與錄音蛔钙,去河邊找鬼。 笑死荠医,一個(gè)胖子當(dāng)著我的面吹牛吁脱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彬向,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼兼贡,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了娃胆?” 一聲冷哼從身側(cè)響起遍希,我...
    開(kāi)封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎里烦,沒(méi)想到半個(gè)月后凿蒜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胁黑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年废封,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丧蘸。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡漂洋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出力喷,到底是詐尸還是另有隱情刽漂,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布弟孟,位于F島的核電站爽冕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏披蕉。R本人自食惡果不足惜颈畸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一乌奇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眯娱,春花似錦礁苗、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至于样,卻和暖如春疏叨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背穿剖。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工蚤蔓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人糊余。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓秀又,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贬芥。 傳聞我的和親對(duì)象是個(gè)殘疾皇子吐辙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容