- M 6.0
- O 8.0
M 6.0
Android 6.0 版本(Api 23)推出了 動(dòng)態(tài)權(quán)限管理。
應(yīng)用權(quán)限簡介
Android
應(yīng)用默認(rèn)情況下不擁有任何權(quán)限,申請(qǐng)權(quán)限要在AndroidManifest.xml
中靜態(tài)聲明。
若未在manifest
中聲明權(quán)限具帮,則在調(diào)用相應(yīng)功能時(shí)蒜绽,將拋出異常,一般不會(huì)catch
該異常包竹,程序會(huì)直接崩潰:
Caused by: java.lang.SecurityException: Permission denied (missing INTERNET permission?)
在** Android 6.0 之前, 所有的權(quán)限都在安裝應(yīng)用時(shí)顯示給用戶,選擇安裝則表示授權(quán)全部權(quán)限周瞎,之后無法取消授權(quán)苗缩。
在 Android 6.0 **之后, 危險(xiǎn)權(quán)限(dangerous)
需在程序運(yùn)行時(shí)顯式彈框声诸,請(qǐng)求用戶授權(quán)酱讶。何時(shí)彈框由應(yīng)用程序決定。
對(duì)于普通權(quán)限(normal)
双絮,仍保持原聲明方式浴麻,在安裝應(yīng)用程序時(shí)予以授權(quán)。
保護(hù)等級(jí)
permission
的保護(hù)等級(jí)通過protectionLevel
屬性設(shè)置囤攀,共4種:
- normal
- dangerous
- signature
- signatureOrSystem
詳見:http://developer.android.com/guide/topics/manifest/permission-element.html
簽名相關(guān)的不常用软免, 剩下normal
和dangerous
官網(wǎng) Guides: https://developer.android.com/guide/topics/security/permissions.html#normal-dangerous
總結(jié)下來:所有的權(quán)限仍在manifest
中靜態(tài)聲明,normal權(quán)限
在安裝時(shí)自動(dòng)授權(quán)焚挠,dangerous權(quán)限
需應(yīng)用明確地請(qǐng)求用戶授權(quán)膏萧。
對(duì)于Android 6.0
以下的手機(jī),或以前開發(fā)的舊應(yīng)用蝌衔,dangerous權(quán)限
也是安裝時(shí)授權(quán)榛泛。
normal 類權(quán)限
當(dāng)安裝或更新時(shí),系統(tǒng)將授予應(yīng)用請(qǐng)求的屬于 PROTECTION_NORMAL
的所有權(quán)限噩斟。只需在AndroidManifest.xml
中聲明曹锨,不必每次使用都檢查權(quán)限,且用戶不能取消以上授權(quán)剃允。
序號(hào) | 權(quán)限 |
---|---|
1 | android.permission.ACCESS_LOCATION_EXTRA_COMMANDS |
2 | android.permission.ACCESS_NETWORK_STATE |
3 | android.permission.ACCESS_NOTIFICATION_POLICY |
4 | android.permission.ACCESS_WIFI_STATE |
5 | android.permission.ACCESS_WIMAX_STATE |
6 | android.permission.BLUETOOTH |
7 | android.permission.BLUETOOTH_ADMIN |
8 | android.permission.BROADCAST_STICKY |
9 | android.permission.CHANGE_NETWORK_STATE |
10 | android.permission.CHANGE_WIFI_MULTICAST_STATE |
11 | android.permission.CHANGE_WIFI_STATE |
12 | android.permission.CHANGE_WIMAX_STATE |
13 | android.permission.DISABLE_KEYGUARD |
14 | android.permission.EXPAND_STATUS_BAR |
15 | android.permission.FLASHLIGHT |
16 | android.permission.GET_ACCOUNTS |
17 | android.permission.GET_PACKAGE_SIZE |
18 | android.permission.INTERNET |
19 | android.permission.KILL_BACKGROUND_PROCESSES |
20 | android.permission.MODIFY_AUDIO_SETTINGS |
21 | android.permission.NFC |
22 | android.permission.READ_SYNC_SETTINGS |
23 | android.permission.READ_SYNC_STATS |
24 | android.permission.RECEIVE_BOOT_COMPLETED |
25 | android.permission.REORDER_TASKS |
26 | android.permission.REQUEST_INSTALL_PACKAGES |
27 | android.permission.SET_TIME_ZONE |
28 | android.permission.SET_WALLPAPER |
29 | android.permission.SET_WALLPAPER_HINTS |
30 | android.permission.SUBSCRIBED_FEEDS_READ |
31 | android.permission.TRANSMIT_IR |
32 | android.permission.USE_FINGERPRINT |
33 | android.permission.VIBRATE |
34 | android.permission.WAKE_LOCK |
35 | android.permission.WRITE_SYNC_SETTINGS |
36 | com.android.alarm.permission.SET_ALARM |
37 | com.android.launcher.permission.INSTALL_SHORTCUT |
38 | com.android.launcher.permission.UNINSTALL_SHORTCUT |
權(quán)限組
新的權(quán)限模型提出了權(quán)限組的概念沛简,即:同權(quán)限組內(nèi)的某個(gè)權(quán)限被授權(quán)了,則該組中剩余的權(quán)限也會(huì)被自動(dòng)獲取授權(quán)斥废。例:Android.permission-group.CALENDAR
權(quán)限組中的android.permission.WRITE_CALENDAR
權(quán)限被授權(quán)椒楣,則應(yīng)用會(huì)自動(dòng)獲取android.permission.READ_CALENDAR
權(quán)限。
序號(hào) | 名稱 | 權(quán)限組 | 權(quán)限 |
---|---|---|---|
1 | 日歷 | android.permission-group.CALENDAR |
android.permission.READ_CALENDAR android.permission.WRITE_CALENDAR
|
2 | 攝像頭 | android.permission-group.CAMERA |
android.permission.CAMERA |
3 | 通訊錄 | android.permission-group.CONTACTS |
android.permission.READ_CONTACTS android.permission.WRITE_CONTACTS android.permission.GET_ACCOUNTS
|
4 | 地理位置 | android.permission-group.LOCATION |
android.permission.ACCESS_FINE_LOCATION android.permission.ACCESS_COARSE_LOCATION
|
5 | 麥克風(fēng) | android.permission-group.MICROPHONE |
android.permission.RECORD_AUDIO |
6 | 電話 | android.permission-group.PHONE |
android.permission.READ_PHONE_STATE android.permission.CALL_PHONE android.permission.READ_CALL_LOG android.permission.WRITE_CALL_LOG com.android.voicemail.permission.ADD_VOICEMAIL android.permission.USE_SIP android.permission.PROCESS_OUTGOING_CALLS
|
7 | 傳感器 | android.permission-group.SENSORS |
android.permission.BODY_SENSORS |
8 | 短信 | android.permission-group.SMS |
android.permission.SEND_SMS android.permission.RECEIVE_SMS android.permission.READ_SMS android.permission.RECEIVE_WAP_PUSH android.permission.RECEIVE_MMS android.permission.READ_CELL_BROADCASTS
|
9 | 存儲(chǔ)空間 | android.permission-group.STORAGE |
android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE
|
動(dòng)態(tài)權(quán)限使用
一
判斷是否有權(quán)限:checkSelfPermission()
二
如果沒有權(quán)限牡肉,彈窗給用戶選擇:requestPermission()
捧灰,第二個(gè)參數(shù)code
與onRequestPermissionResult()
方法中的code
對(duì)應(yīng)。
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
三
彈出權(quán)限選擇對(duì)話框前彈出提示统锤,用于引導(dǎo)用戶選擇:shouldShowRequestPermissionRationale()
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity.this, Manifest.permission.CAMERA)) {
Snackbar.make(view, "請(qǐng)?jiān)试S應(yīng)用使用照相機(jī)", Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
})
.show();
} else {
ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
}
四
判斷用戶是否已確認(rèn)權(quán)限:onRequestPermissionResult()
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CAMERA:
if (PermissionUtil.verifyPermissions(grantResults)) {
Snackbar.make(mLayout, "同意授權(quán)", Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(mLayout, "拒絕授權(quán)", Snackbar.LENGTH_SHORT).show();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
五
若用戶在選擇權(quán)限對(duì)話框時(shí)拒絕了某個(gè)權(quán)限的申請(qǐng)毛俏,那么再次申請(qǐng)?jiān)摍?quán)限時(shí)會(huì)多出一個(gè)“不再詢問”的checkbox
,若勾選饲窿,則程序再調(diào)用requestPermission()
拧抖,對(duì)話框也不會(huì)彈出。
六
同時(shí)處理多個(gè)權(quán)限免绿,方案待定
兼容問題
一. 動(dòng)態(tài)權(quán)限檢查需在android 6.0
版本以上運(yùn)行,即api 23
之前不能運(yùn)行:
- 判斷
SDK
版本號(hào)
public static boolean checkSDKVersion_23() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M;
}
- 使用
v4
兼容庫擦盾,兼容所有版本
ContextCompat.checkSelfPermission()
授權(quán)返回PERMISSION_GRANTED
嘲驾,否則返回PERMISSION_DENIED
淌哟,所有版本都是如此。ActivityCompat.requestPermissions()
在M
之前版本調(diào)用辽故,OnRequestPermissionsResultCallback
直接被調(diào)用徒仓,帶著正確的PERMISSION_GRANTED
或PERMISSION_DENIED
。ActivityCompat.shouldShowRequestPermissionRationale()
在M
之前版本調(diào)用誊垢,永遠(yuǎn)返回false
掉弛。
二. 在Fragment
中使用,用v13
兼容包喂走,效果一樣:
FragmentCompat.requestPermissions()
FragmentCompat.shouldShowRequestPermissionRationale()
-
Fragment
中嵌套Fragment
殃饿,子Fragment
中建議用getParentFragment().requestPermissions
方法回調(diào)父Fragment
中的onRequestPermissionsResult
,添加以下代碼將回調(diào)透傳到子Fragment
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
List<Fragment> fragmentList = getChildFragmentManager().getFragments();
if (fragmentList != null && fragmentList.size > 0) {
for (Fragment fragment : fragmentList ) {
if (fragment != null) {
fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
}
}
}
}
開源
PermissionsDispatcher 使用標(biāo)注方式暫不支持嵌套Fragment
RxPermissions 基于RxJava
android-RuntimePermissions 谷歌官方示例
參考
Android M 動(dòng)態(tài)權(quán)限獲取
O 8.0
官方:
在 Android 8.0 之前芋肠,如果應(yīng)用在運(yùn)行時(shí)請(qǐng)求權(quán)限并且被授予該權(quán)限乎芳,系統(tǒng)會(huì)錯(cuò)誤地將屬于同一權(quán)限組并且在清單中注冊(cè)的其他權(quán)限也一起授予應(yīng)用。
對(duì)于針對(duì) Android 8.0 的應(yīng)用帖池,此行為已被糾正奈惑。系統(tǒng)只會(huì)授予應(yīng)用明確請(qǐng)求的權(quán)限。然而睡汹,一旦用戶為應(yīng)用授予某個(gè)權(quán)限肴甸,則所有后續(xù)對(duì)該權(quán)限組中權(quán)限的請(qǐng)求都將被自動(dòng)批準(zhǔn)。
例如囚巴,假設(shè)某個(gè)應(yīng)用在其清單中列出 READ_EXTERNAL_STORAGE
原在。應(yīng)用請(qǐng)求 READ_EXTERNAL_STORAGE
,并且用戶授予了該權(quán)限文兢。如果該應(yīng)用針對(duì)的是 API 級(jí)別 24 或更低級(jí)別晤斩,系統(tǒng)還會(huì)同時(shí)授予 WRITE_EXTERNAL_STORAGE
,因?yàn)樵摍?quán)限也屬于同一 STORAGE
權(quán)限組并且也在清單中注冊(cè)過姆坚。如果該應(yīng)用針對(duì)的是 Android 8.0澳泵,則系統(tǒng)此時(shí)僅會(huì)授予 READ_EXTERNAL_STORAGE
;不過兼呵,如果該應(yīng)用后來又請(qǐng)求 WRITE_EXTERNAL_STORAGE
兔辅,則系統(tǒng)會(huì)立即授予該權(quán)限,而不會(huì)提示用戶击喂。