系統(tǒng)權(quán)限分為兩類:正常權(quán)限和危險(xiǎn)權(quán)限:
- 正常權(quán)限不會(huì)直接給用戶隱私權(quán)帶來(lái)風(fēng)險(xiǎn)针贬。如果您的應(yīng)用在其清單中列出了正常權(quán)限,系統(tǒng)將自動(dòng)授予該權(quán)限赋兵。
- 危險(xiǎn)權(quán)限會(huì)授予應(yīng)用訪問(wèn)用戶機(jī)密數(shù)據(jù)的權(quán)限笔咽。如果您的應(yīng)用在其清單中列出了正常權(quán)限,系統(tǒng)將自動(dòng)授予該權(quán)限霹期。如果您列出了危險(xiǎn)權(quán)限叶组,則用戶必須明確批準(zhǔn)您的應(yīng)用使用這些權(quán)限。
如需了解詳細(xì)信息历造,請(qǐng)參閱正常權(quán)限和危險(xiǎn)權(quán)限甩十。
在所有版本的 Android 中船庇,您的應(yīng)用都需要在其應(yīng)用清單中同時(shí)聲明它需要的正常權(quán)限和危險(xiǎn)權(quán)限,如聲明權(quán)限中所述侣监。不過(guò)鸭轮,該聲明的影響因系統(tǒng)版本和應(yīng)用的目標(biāo) SDK 級(jí)別的不同而有所差異:
- 如果設(shè)備運(yùn)行的是 Android 5.1 或更低版本,或者應(yīng)用的目標(biāo) SDK 為 22 或更低:如果您在清單中列出了危險(xiǎn)權(quán)限橄霉,則用戶必須在安裝應(yīng)用時(shí)授予此權(quán)限窃爷;如果他們不授予此權(quán)限,系統(tǒng)根本不會(huì)安裝應(yīng)用姓蜂。
- 如果設(shè)備運(yùn)行的是 Android 6.0 或更高版本按厘,或者應(yīng)用的目標(biāo) SDK 為 23 或更高:應(yīng)用必須在清單中列出權(quán)限,并且它必須在運(yùn)行時(shí)請(qǐng)求其需要的每項(xiàng)危險(xiǎn)權(quán)限钱慢。用戶可以授予或拒絕每項(xiàng)權(quán)限刻剥,且即使用戶拒絕權(quán)限請(qǐng)求,應(yīng)用仍可以繼續(xù)運(yùn)行有限的功能滩字。
注:從 Android 6.0(API 級(jí)別 23)開(kāi)始,用戶可以隨時(shí)從任意應(yīng)用調(diào)用權(quán)限御吞,即使應(yīng)用面向較低的 API 級(jí)別也可以調(diào)用麦箍。無(wú)論您的應(yīng)用面向哪個(gè) API 級(jí)別,您都應(yīng)對(duì)應(yīng)用進(jìn)行測(cè)試陶珠,以驗(yàn)證它在缺少需要的權(quán)限時(shí)行為是否正常挟裂。
本課將介紹如何使用 Android 支持庫(kù)來(lái)檢查和請(qǐng)求權(quán)限。Android 框架從 Android 6.0(API 級(jí)別 23)開(kāi)始提供類似方法揍诽。不過(guò)诀蓉,使用支持庫(kù)更簡(jiǎn)單,因?yàn)樵谡{(diào)用方法前暑脆,您的應(yīng)用不需要檢查它在哪個(gè)版本的 Android 上運(yùn)行渠啤。
檢查權(quán)限
如果您的應(yīng)用需要危險(xiǎn)權(quán)限,則每次執(zhí)行需要這一權(quán)限的操作時(shí)您都必須檢查自己是否具有該權(quán)限添吗。用戶始終可以自由調(diào)用此權(quán)限沥曹,因此,即使應(yīng)用昨天使用了相機(jī)碟联,它不能假設(shè)自己今天仍具有該權(quán)限妓美。
要檢查您是否具有某項(xiàng)權(quán)限,請(qǐng)調(diào)用 ContextCompat.checkSelfPermission()
方法鲤孵。例如壶栋,以下代碼段顯示了如何檢查 Activity 是否具有在日歷中進(jìn)行寫入的權(quán)限:
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.WRITE_CALENDAR);
如果應(yīng)用具有此權(quán)限,方法將返回 PackageManager.PERMISSION_GRANTED普监,并且應(yīng)用可以繼續(xù)操作贵试。如果應(yīng)用不具有此權(quán)限琉兜,方法將返回 PERMISSION_DENIED,且應(yīng)用必須明確向用戶要求權(quán)限锡移。
請(qǐng)求權(quán)限
如果您的應(yīng)用需要應(yīng)用清單中列出的危險(xiǎn)權(quán)限呕童,那么,它必須要求用戶授予該權(quán)限淆珊。Android 為您提供了多種權(quán)限請(qǐng)求方式夺饲。調(diào)用這些方法將顯示一個(gè)標(biāo)準(zhǔn)的 Android 對(duì)話框,不過(guò)施符,您不能對(duì)它們進(jìn)行自定義往声。
解釋應(yīng)用為什么需要權(quán)限
圖 1. 提示用戶授予或拒絕權(quán)限的系統(tǒng)對(duì)話框。
在某些情況下戳吝,您可能需要幫助用戶了解您的應(yīng)用為什么需要某項(xiàng)權(quán)限浩销。例如,如果用戶啟動(dòng)一個(gè)攝影應(yīng)用听哭,用戶對(duì)應(yīng)用要求使用相機(jī)的權(quán)限可能不會(huì)感到吃驚慢洋,但用戶可能無(wú)法理解為什么此應(yīng)用想要訪問(wèn)用戶的位置或聯(lián)系人。在請(qǐng)求權(quán)限之前陆盘,不妨為用戶提供一個(gè)解釋普筹。請(qǐng)記住,您不需要通過(guò)解釋來(lái)說(shuō)服用戶隘马;如果您提供太多解釋太防,用戶可能發(fā)現(xiàn)應(yīng)用令人失望并將其移除。
您可以采用的一個(gè)方法是僅在用戶已拒絕某項(xiàng)權(quán)限請(qǐng)求時(shí)提供解釋酸员。如果用戶繼續(xù)嘗試使用需要某項(xiàng)權(quán)限的功能蜒车,但繼續(xù)拒絕權(quán)限請(qǐng)求,則可能表明用戶不理解應(yīng)用為什么需要此權(quán)限才能提供相關(guān)功能幔嗦。對(duì)于這種情況酿愧,比較好的做法是顯示解釋。
為了幫助查找用戶可能需要解釋的情形邀泉,Android 提供了一個(gè)實(shí)用程序方法寓娩,即 [shouldShowRequestPermissionRationale()](https://developer.android.google.cn/reference/android/support/v4/app/ActivityCompat.html#shouldShowRequestPermissionRationale(android.app.Activity, java.lang.String))。如果應(yīng)用之前請(qǐng)求過(guò)此權(quán)限但用戶拒絕了請(qǐng)求呼渣,此方法將返回 true
棘伴。
注:如果用戶在過(guò)去拒絕了權(quán)限請(qǐng)求,并在權(quán)限請(qǐng)求系統(tǒng)對(duì)話框中選擇了 Don't ask again 選項(xiàng)屁置,此方法將返回
false
焊夸。如果設(shè)備規(guī)范禁止應(yīng)用具有該權(quán)限,此方法也會(huì)返回false
蓝角。
請(qǐng)求您需要的權(quán)限
如果應(yīng)用尚無(wú)所需的權(quán)限阱穗,則應(yīng)用必須調(diào)用一個(gè) requestPermissions()
方法饭冬,以請(qǐng)求適當(dāng)?shù)臋?quán)限。應(yīng)用將傳遞其所需的權(quán)限揪阶,以及您指定用于識(shí)別此權(quán)限請(qǐng)求的整型請(qǐng)求代碼昌抠。此方法異步運(yùn)行:它會(huì)立即返回,并且在用戶響應(yīng)對(duì)話框之后鲁僚,系統(tǒng)會(huì)使用結(jié)果調(diào)用應(yīng)用的回調(diào)方法炊苫,將應(yīng)用傳遞的相同請(qǐng)求代碼傳遞到 requestPermissions()
。
以下代碼可以檢查應(yīng)用是否具備讀取用戶聯(lián)系人的權(quán)限冰沙,并根據(jù)需要請(qǐng)求該權(quán)限:
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}
注:當(dāng)您的應(yīng)用調(diào)用
requestPermissions()
時(shí)侨艾,系統(tǒng)將向用戶顯示一個(gè)標(biāo)準(zhǔn)對(duì)話框。您的應(yīng)用無(wú)法配置或更改此對(duì)話框拓挥。如果您需要為用戶提供任何信息或解釋唠梨,您應(yīng)在調(diào)用requestPermissions()
之前進(jìn)行,如解釋應(yīng)用為什么需要權(quán)限中所述侥啤。
處理權(quán)限請(qǐng)求響應(yīng)
當(dāng)應(yīng)用請(qǐng)求權(quán)限時(shí)当叭,系統(tǒng)將向用戶顯示一個(gè)對(duì)話框。當(dāng)用戶響應(yīng)時(shí)盖灸,系統(tǒng)將調(diào)用應(yīng)用的 onRequestPermissionsResult()
方法科展,向其傳遞用戶響應(yīng)。您的應(yīng)用必須替換該方法糠雨,以了解是否已獲得相應(yīng)權(quán)限∨枪颍回調(diào)會(huì)將您傳遞的相同請(qǐng)求代碼傳遞給 requestPermissions()
甘邀。例如,如果應(yīng)用請(qǐng)求 READ_CONTACTS 訪問(wèn)權(quán)限垮庐,則它可能采用以下回調(diào)方法:
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the
// contacts-related task you need to do.
} else {
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
系統(tǒng)顯示的對(duì)話框說(shuō)明了您的應(yīng)用需要訪問(wèn)的權(quán)限組松邪;它不會(huì)列出具體權(quán)限。例如哨查,如果您請(qǐng)求 READ_CONTACTS 權(quán)限逗抑,系統(tǒng)對(duì)話框只顯示您的應(yīng)用需要訪問(wèn)設(shè)備的聯(lián)系人。用戶只需要為每個(gè)權(quán)限組授予一次權(quán)限寒亥。如果您的應(yīng)用請(qǐng)求該組中的任何其他權(quán)限(已在您的應(yīng)用清單中列出)邮府,系統(tǒng)將自動(dòng)授予應(yīng)用這些權(quán)限。當(dāng)您請(qǐng)求此權(quán)限時(shí)溉奕,系統(tǒng)會(huì)調(diào)用您的 onRequestPermissionsResult()
回調(diào)方法褂傀,并傳遞 PERMISSION_GRANTED
,如果用戶已通過(guò)系統(tǒng)對(duì)話框明確同意您的權(quán)限請(qǐng)求加勤,系統(tǒng)將采用相同方式操作仙辟。
注:您的應(yīng)用仍需要明確請(qǐng)求其需要的每項(xiàng)權(quán)限同波,即使用戶已向應(yīng)用授予該權(quán)限組中的其他權(quán)限。此外叠国,權(quán)限分組在將來(lái)的 Android 版本中可能會(huì)發(fā)生變化未檩。您的代碼不應(yīng)依賴特定權(quán)限屬于或不屬于相同組這種假設(shè)。
例如粟焊,假設(shè)您在應(yīng)用清單中列出了 READ_CONTACTS 和 WRITE_CONTACTS冤狡。如果您請(qǐng)求 READ_CONTACTS 且用戶授予了此權(quán)限,那么吆玖,當(dāng)您請(qǐng)求 WRITE_CONTACTS 時(shí)筒溃,系統(tǒng)將立即授予您該權(quán)限,不會(huì)與用戶交互沾乘。
如果用戶拒絕了某項(xiàng)權(quán)限請(qǐng)求怜奖,您的應(yīng)用應(yīng)采取適當(dāng)?shù)牟僮鳌@绯嵴螅膽?yīng)用可能顯示一個(gè)對(duì)話框歪玲,解釋它為什么無(wú)法執(zhí)行用戶已經(jīng)請(qǐng)求但需要該權(quán)限的操作。
當(dāng)系統(tǒng)要求用戶授予權(quán)限時(shí)掷匠,用戶可以選擇指示系統(tǒng)不再要求提供該權(quán)限滥崩。這種情況下,無(wú)論應(yīng)用在什么時(shí)候使用 requestPermissions()
再次要求該權(quán)限讹语,系統(tǒng)都會(huì)立即拒絕此請(qǐng)求钙皮。系統(tǒng)會(huì)調(diào)用您的 onRequestPermissionsResult()
回調(diào)方法,并傳遞 PERMISSION_DENIED顽决,如果用戶再次明確拒絕了您的請(qǐng)求短条,系統(tǒng)將采用相同方式操作。這意味著當(dāng)您調(diào)用 requestPermissions()
時(shí)才菠,您不能假設(shè)已經(jīng)發(fā)生與用戶的任何直接交互茸时。