- M 6.0
- O 8.0
M 6.0
Android 6.0 版本(Api 23)推出了 動態(tài)權(quán)限管理。
應(yīng)用權(quán)限簡介
Android應(yīng)用默認(rèn)情況下不擁有任何權(quán)限,申請權(quán)限要在AndroidManifest.xml中靜態(tài)聲明。
若未在manifest中聲明權(quán)限,則在調(diào)用相應(yīng)功能時(shí),將拋出異常,一般不會catch該異常,程序會直接崩潰:
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í)顯式彈框,請求用戶授權(quán)。何時(shí)彈框由應(yīng)用程序決定。
對于普通權(quán)限(normal),仍保持原聲明方式,在安裝應(yīng)用程序時(shí)予以授權(quán)。
保護(hù)等級
permission的保護(hù)等級通過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í)自動授權(quán),dangerous權(quán)限需應(yīng)用明確地請求用戶授權(quán)。
對于Android 6.0以下的手機(jī),或以前開發(fā)的舊應(yīng)用,dangerous權(quán)限也是安裝時(shí)授權(quán)。


normal 類權(quán)限
當(dāng)安裝或更新時(shí),系統(tǒng)將授予應(yīng)用請求的屬于 PROTECTION_NORMAL 的所有權(quán)限。只需在AndroidManifest.xml中聲明,不必每次使用都檢查權(quán)限,且用戶不能取消以上授權(quán)。
| 序號 | 權(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)的某個權(quán)限被授權(quán)了,則該組中剩余的權(quán)限也會被自動獲取授權(quán)。例:Android.permission-group.CALENDAR權(quán)限組中的android.permission.WRITE_CALENDAR權(quán)限被授權(quán),則應(yīng)用會自動獲取android.permission.READ_CALENDAR權(quán)限。
| 序號 | 名稱 | 權(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 | 存儲空間 | android.permission-group.STORAGE |
android.permission.READ_EXTERNAL_STORAGE android.permission.WRITE_EXTERNAL_STORAGE
|
動態(tài)權(quán)限使用
一
判斷是否有權(quán)限:checkSelfPermission()
二
如果沒有權(quán)限,彈窗給用戶選擇:requestPermission(),第二個參數(shù)code與onRequestPermissionResult()方法中的code對應(yīng)。
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(new String[] { Manifest.permission.CAMERA}, REQUEST_CAMERA);
}
三
彈出權(quán)限選擇對話框前彈出提示,用于引導(dǎo)用戶選擇:shouldShowRequestPermissionRationale()
if(ContextCompat.checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity.this, Manifest.permission.CAMERA)) {
Snackbar.make(view, "請?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)限對話框時(shí)拒絕了某個權(quán)限的申請,那么再次申請?jiān)摍?quán)限時(shí)會多出一個“不再詢問”的checkbox,若勾選,則程序再調(diào)用requestPermission(),對話框也不會彈出。

六
同時(shí)處理多個權(quán)限,方案待定
兼容問題
一. 動態(tài)權(quán)限檢查需在android 6.0版本以上運(yùn)行,即api 23之前不能運(yùn)行:
- 判斷
SDK版本號
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 谷歌官方示例
參考
O 8.0
官方:
在 Android 8.0 之前,如果應(yīng)用在運(yùn)行時(shí)請求權(quán)限并且被授予該權(quán)限,系統(tǒng)會錯誤地將屬于同一權(quán)限組并且在清單中注冊的其他權(quán)限也一起授予應(yīng)用。
對于針對 Android 8.0 的應(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。應(yīng)用請求 READ_EXTERNAL_STORAGE,并且用戶授予了該權(quán)限。如果該應(yīng)用針對的是 API 級別 24 或更低級別,系統(tǒng)還會同時(shí)授予 WRITE_EXTERNAL_STORAGE,因?yàn)樵摍?quán)限也屬于同一 STORAGE 權(quán)限組并且也在清單中注冊過。如果該應(yīng)用針對的是 Android 8.0,則系統(tǒng)此時(shí)僅會授予 READ_EXTERNAL_STORAGE;不過,如果該應(yīng)用后來又請求 WRITE_EXTERNAL_STORAGE,則系統(tǒng)會立即授予該權(quán)限,而不會提示用戶。