Android M runtime permissions(Android 6.0執行時許可權)
官方介紹:
ofollow,noindex">https://developer.android.com/training/permissions/requesting?hl=zh-cnGoogle Sample地址:
https://github.com/googlesamples/android-RuntimePermissionsBasic
https://github.com/googlesamples/android-RuntimePermissionsAndroid M引入了“執行時許可權”,targetSdkVersion 23及以上的應用不但需要在AndroidManifest.xml中宣告它們需要的許可權,還需要在執行時動態申請這些已宣告過的危險許可權;targetSdkVersion低於23的應用,在安裝時使用者已經同意了所有許可權,並且在執行時所需許可權全部可用。如果不同意這些許可權,使用者將無法安裝此應用。
處理過程包括:
-
在應用的清單檔案中新增使用具體功能需要的相關許可權;
-
每次在使用需要危險許可權的功能之前,檢查應用是否具有相關許可權(使用者有可能在授權之後手動撤銷許可權);
- 如果應用不具有相關許可權,申請相關許可權;
- 如果應用已擁有相關許可權,開始使用具體功能;
- 申請許可權之前,幫助使用者瞭解應用為什麼需要某項許可權;
- 如果使用者曾經拒絕授權,彈窗引導使用者進行動態申請許可權;
- 如果使用者未授權,申請相關許可權;
- 如果使用者曾經拒絕授權且勾選“不再提示”選項(“不再提示”選項僅在使用者已拒絕過1次授權此許可權後應用再次申請使用者授權此許可權時顯示)而導致無法顯示彈窗,提示相關許可權申請失敗,無法使用具體功能,並告知使用者開啟相關許可權的操作方法(設定->應用->選擇應用->許可權);
-
監聽申請許可權的結果,如果使用者同意應用申請的許可權後,開始使用具體功能;如果使用者拒絕應用申請的許可權,提示相關許可權申請失敗,無法使用具體功能。
注:當用戶授權應用同一組危險許可權中的某一項許可權後(如果使用者不手動撤銷此許可權),系統將自動授權應用該組的其他許可權。後期版本許可權組可能會發生變化。
場景:使用相機預覽
- 在AndroidMinifest的根節點下宣告相機許可權
<uses-permissionandroid:name="android.permission.CAMERA"/>
-
通過點選按鈕,觸發“開啟相機預覽”事件
-
進行許可權驗證
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) { requestCameraPermission(); } else { startCamera();// 假裝有相機 }
- 申請許可權
private void requestCameraPermission() { // Permission has not been granted and must be requested. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { //應用之前請求過此許可權但使用者拒絕了請求 Snackbar.make(mLayout, R.string.camera_access_required, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.ok, new View.OnClickListener() { @Override public void onClick(View view) { // Request the permission ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA); } }).show(); } else { // 應用未請求過此許可權或者使用者在過去拒絕了許可權請求,並在許可權請求系統對話方塊中選擇了“不再提示”選項 Snackbar.make(mLayout, R.string.camera_unavailable, Snackbar.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA); } }
- 監聽申請許可權結果
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == PERMISSION_REQUEST_CAMERA) { // Request for camera permission. if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission has been granted. Start camera preview Activity. Snackbar.make(mLayout, R.string.camera_permission_granted, Snackbar.LENGTH_SHORT).show(); startCamera(); } else { // Permission request was denied. Snackbar.make(mLayout, R.string.camera_permission_denied, Snackbar.LENGTH_SHORT).show(); } } }
- Fragment中驗證授權和申請授權操作
- 使用
ActivityCompat.requestPermissions(getActivity(), String[], int)
,會執行Activity的onRequestPermissionsResult()
方法; - 直接使用
Fragment.requestPermissions(String[], int)
,會執行Fragment的onRequestPermissionsResult()
方法;
- 巢狀Fragment中申請許可權操作
getParentFragment().requestPermissions() onRequestPermissionsResutl()
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); List<Fragment> fragments = getChildFragmentManager().getFragments(); if (fragments != null) { for (Fragmentfragment : fragments) { if (fragment != null) { fragment.onRequestPermissionsResult(requestCode, permissions,grantResults); } } } }
應用targetSdkVersion < 23的相容性處理:
場景:
targetSdkVersion < 23的App安裝在Android 6.0及以上裝置時,使用者已同意App中宣告的所有許可權,但是在安裝完成後,使用者手動在設定中關閉了APP的許可權。此時呼叫 ContextCompat.checkSelfPermission()
檢查APP是否擁有指定許可權,返回值仍為 PackageManager.PERMISSION_GRANTED
,如果使用需要此項危險許可權的功能,應用將奔潰。
解決:
- 方案一:升級targetSdkVersion到23或23以上版本;
- 方案二:
(Build,VERSION.SDK_INT >= Build.VERSION_CODES.M)
try { PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); int targetSdkVersion = info.applicationInfo.targetSdkVersion; } catch (PackageManager.NameNotFoundExceptione) { e.printStackTrace(); }
a) 如果targetSdkVersion 低於23,使用PermissionChecker相容庫檢查APP擁有的許可權;
b) 如果targetSdkVersion 不低於23,使用之前的方式檢查APP是否擁有指定許可權;
- 呼叫
PermissionChecker.checkSelfPermission()
檢查APP是否擁有指定許可權,返回值:
a) PERMISSION_GRANTED
:已授權
b) PERMISSION_DENIED
:使用者拒絕授權
c) PERMISSION_DENIED_APP_OP
:使用者拒絕授權且targetSdkVersion小於23
runtime permissions處理封裝庫:
鴻洋MPermissions: https://github.com/hongyangAndroid/MPermissions
- 引入
- project’s build.gradle
buildscript { dependencies { classpath'com.neenbedankt.gradle.plugins:android-apt:1.4' } }
- module’s build.gradle
apply plugin: 'com.neenbedankt.android-apt' dependencies { apt 'com.zhy:mpermission-compiler:1.0.0' compile 'com.zhy:mpermission-api:1.0.0' }
- 定義許可權請求碼
final int REQUECT_CODE_SDCARD = 1;
- 判斷是否需要彈出解釋
if(!MPermissions.shouldShowRequestPermissionRationale(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE, REQUECT_CODE_SDCARD)) { MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE); }
- 申請讀寫SD卡許可權
MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);
- 處理申請許可權回撥
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @PermissionGrant(REQUECT_CODE_SDCARD) public voidrequestSdcardSuccess() { Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show(); } @PermissionDenied(REQUECT_CODE_SDCARD) public void requestSdcardFailed() { Toast.makeText(this, "DENY ACCESS SDCARD!",Toast.LENGTH_SHORT).show(); }
- 混淆
-dontwarn com.zhy.m.** -keep class com.zhy.m.** {*;} -keep interface com.zhy.m.** { *; } -keep class **$$PermissionProxy { *; }
PermissionsDispatcher: https://github.com/hotchemi/PermissionsDispatcher
- 引入:
- module’s build.gradle
dependencies { implementation 'com.github.hotchemi:permissionsdispatcher:${latest.version}' annotationProcessor 'com.github,hotchemi:permissionsdispatcher-processor:${latest.version}' }
-
安裝PermissionsDispatcher外掛
File->Settings->Plugins,搜尋PermissionsDispatcher,點選install安裝,重啟AS。
image.png
-
生成申請許可權操作程式碼
ⅰ. Code->Generate->GenerateRuntime Permissions
image.png
ⅱ. 勾選要申請的許可權以及要覆蓋的方法,點選Generate生成程式碼
@NeedsPermission
使用者同意授權應用相關許可權後的回撥;
@OnShowRationale
使用者已拒絕過1次授權此許可權後,應用再次請求使用者授權此許可權時的回撥。在此方法中解釋應用為什麼需要此許可權。之後呼叫PermissionRequest的 proceed()
方法繼續申請許可權;
@OnPermissionDenied
使用者拒絕授權應用相關許可權後的回撥;
@OnNeverAskAgain
當用戶拒絕授權並勾選“不再提示”後的回撥
- 申請許可權
xxActivityPermissionsDispatcher.**WithPermissionCheck(this);//xx為自動生成的輔助類,**為被@NeedsPermission註解的方法名
常規許可權列表:
ACCESS_LOCATION_EXTRA_COMMANDSACCESS_NETWORK_STATEACCESS_NOTIFICATION_POLICYACCESS_WIFI_STATEBLUETOOTHBLUETOOTH_ADMINBROADCAST_STICKYCHANGE_NETWORK_STATECHANGE_WIFI_MULTICAST_STATECHANGE_WIFI_STATEDISABLE_KEYGUARDEXPAND_STATUS_BARGET_PACKAGE_SIZEINSTALL_SHORTCUTINTERNETKILL_BACKGROUND_PROCESSESMODIFY_AUDIO_SETTINGSNFCREAD_SYNC_SETTINGSREAD_SYNC_STATSRECEIVE_BOOT_COMPLETEDREORDER_TASKSREQUEST_INSTALL_PACKAGESSET_ALARMSET_TIME_ZONESET_WALLPAPERSET_WALLPAPER_HINTSTRANSMIT_IRUNINSTALL_SHORTCUTUSE_FINGERPRINTVIBRATEWAKE_LOCKWRITE_SYNC_SETTINGS
危險許可權列表:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTSpermission:android.permission.GET_ACCOUNTSpermission:android.permission.READ_CONTACTS
group:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOGpermission:android.permission.READ_PHONE_STATEpermission:android.permission.CALL_PHONEpermission:android.permission.WRITE_CALL_LOGpermission:android.permission.USE_SIPpermission:android.permission.PROCESS_OUTGOING_CALLSpermission:com.android.voicemail.permission.ADD_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDARpermission:android.permission.WRITE_CALENDAR
group:android.permission-group.CAMERA
permission:android.permission.CAMERA
group:android.permission-group.SENSORS
permission:android.permission.BODY_SENSORS
group:android.permission-group.LOCATION
permission:android.permission.ACCESS_FINE_LOCATIONpermission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGEpermission:android.permission.WRITE_EXTERNAL_STORAGE
group:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMSpermission:android.permission.RECEIVE_WAP_PUSHpermission:android.permission.RECEIVE_MMSpermission:android.permission.RECEIVE_SMSpermission:android.permission.SEND_SMSpermission:android.permission.READ_CELL_BROADCASTS
匯入Google Sample—RuntimePermissions時,build失敗解決方案:
- 原因:settings.gradle中引入的模組為Application,但是Application缺少build.gradle。
- 解決:File->Open->RuntimePermissions專案->kotlinApp,build後(AS可能需要下載kotlin外掛,允許後等待下載完成)可以正常執行專案。