官方介绍:
https://developer.android.com/training/permissions/requesting?hl=zh-cn
Google Sample地址:
https://github.com/googlesamples/android-RuntimePermissionsBasic
https://github.com/googlesamples/android-RuntimePermissions
Android 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中申请权限操作
- 在子Fragment中使用
getParentFragment().requestPermissions()
申请权限; - 在父Fragment中将
onRequestPermissionsResutl()
回调透传到子Fragmnet
@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以上版本;
- 方案二:
- 获取设备Android版本,如果
(Build,VERSION.SDK_INT >= Build.VERSION_CODES.M)
条件成立,表示应用运行在Android 6.0及更高版本的设备上; - 获取应用targetSdkVersion
- 获取设备Android版本,如果
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_COMMANDS
ACCESS_NETWORK_STATE
ACCESS_NOTIFICATION_POLICY
ACCESS_WIFI_STATE
BLUETOOTH
BLUETOOTH_ADMIN
BROADCAST_STICKY
CHANGE_NETWORK_STATE
CHANGE_WIFI_MULTICAST_STATE
CHANGE_WIFI_STATE
DISABLE_KEYGUARD
EXPAND_STATUS_BAR
GET_PACKAGE_SIZE
INSTALL_SHORTCUT
INTERNET
KILL_BACKGROUND_PROCESSES
MODIFY_AUDIO_SETTINGS
NFC
READ_SYNC_SETTINGS
READ_SYNC_STATS
RECEIVE_BOOT_COMPLETED
REORDER_TASKS
REQUEST_INSTALL_PACKAGES
SET_ALARM
SET_TIME_ZONE
SET_WALLPAPER
SET_WALLPAPER_HINTS
TRANSMIT_IR
UNINSTALL_SHORTCUT
USE_FINGERPRINT
VIBRATE
WAKE_LOCK
WRITE_SYNC_SETTINGS
危险权限列表:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTSgroup:android.permission-group.PHONE
permission:android.permission.READ_CALL_LOG
permission:android.permission.READ_PHONE_STATE
permission:android.permission.CALL_PHONE
permission:android.permission.WRITE_CALL_LOG
permission:android.permission.USE_SIP
permission:android.permission.PROCESS_OUTGOING_CALLS
permission:com.android.voicemail.permission.ADD_VOICEMAILgroup:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission:android.permission.WRITE_CALENDARgroup: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_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATIONgroup:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGEgroup:android.permission-group.MICROPHONE
permission:android.permission.RECORD_AUDIO
group:android.permission-group.SMS
permission:android.permission.READ_SMS
permission:android.permission.RECEIVE_WAP_PUSH
permission:android.permission.RECEIVE_MMS
permission:android.permission.RECEIVE_SMS
permission:android.permission.SEND_SMS
permission:android.permission.READ_CELL_BROADCASTS
导入Google Sample—RuntimePermissions时,build失败解决方案:
- 原因:settings.gradle中引入的模块为Application,但是Application缺少build.gradle。
- 解决:File->Open->RuntimePermissions项目->kotlinApp,build后(AS可能需要下载kotlin插件,允许后等待下载完成)可以正常运行项目。
网友评论