转载请申明出处:
前言:Android M (API 23)起,权限管理有了很大的改变,引入了运行时权限检测,并将权限大致分为普通和危险两类,当应用首次使用某一危险的权限时,需要弹出授权对话框,让用户进行二次确认。注意权限询问对话框不会自己弹出来。开发者不得不自己调用。将应用的target SDK版本设置为23,并在6.0的模拟器或设备上运行。如果开发者要调用的一些函数需要某权限而用户又拒绝授权的话,函数将抛出异常直接导致程序崩溃。
一、权限的分类
Google将权限分为两类:
1、Normal Permissions。这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等,下面列出普通权限。
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
2、Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等。
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group: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_VOICEMAIL
group:android.permission-group.CALENDAR
permission:android.permission.READ_CALENDAR
permission: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_LOCATION
permission:android.permission.ACCESS_COARSE_LOCATION
group:android.permission-group.STORAGE
permission:android.permission.READ_EXTERNAL_STORAGE
permission:android.permission.WRITE_EXTERNAL_STORAGE
group: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
二、权限的管理
在安卓6.0以下版本中,开发者只需要将应用程序所需要的权限列在清单配置文件中,用户首次安装时会将所有权限展示给用户并让用户对危险权限授权。这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。
6.0以后,相比起以前将应用权限一股脑都列出来,让用户决定装还是不装,Android 6.0 的权限管理会在应用需要用到危险权限的时候弹窗提醒,让用户进行授权,用户授权后,应用才能得到相应的权限。而提醒的弹框并不会自动弹出,这就需要开发者调用API来向用户请求权限,也就是今天我们的任务。
三、请求权限
-
在AndroidManifest文件中添加需要的权限。
这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。
- 检查权限是否已经授权
if(ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
!=PackageManager.PERMISSION_GRANTED){
//如果权限未授权,请求授权
}else {
//如果已经授权
}
这里涉及到一个API,ContextCompat.checkSelfPermission
,主要用于检测某个权限是否已经被授予,方法返回值为PackageManager.PERMISSION_DENIED
或者PackageManager.PERMISSION_GRANTED
。当返回DENIED就需要进行申请授权了。
3.申请授权
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions
以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
4.处理权限申请回调。
@Override
public void onRequestPermissionsResult(int requestCode
, @NonNull String[] permissions
, @NonNull int[] grantResults) {
switch (requestCode){
case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE :
//如果请求被拒绝 grantResults是空的
if(grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED){
//做读写内存卡的操作
}else {
//请求被拒绝了,提示用户不能进行此操作。
}
}
}
到此,我们请求权限就已经完成了。但是还有一个API需要提一下,这个API主要用于给用户一个申请权限的解释,该方法只有在用户在上一次已经拒绝过你的这个权限申请。也就是说,用户已经拒绝一次了,你又弹个授权框,你需要给用户一个解释,为什么要授权,则使用该方法。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_EXTERNAL_STORAGE)){
// 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.
}
完整的代码如下
private void userPermission(){
//如果targetSDK版本号大于23,请求用户授权
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
//如果用户未授权
if(ContextCompat.checkSelfPermission(this
,Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
//如果需要再次提示用户授权,给用户解释
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_EXTERNAL_STORAGE)){
// 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 {
//不需要给用户解释,申请授权
String[] permissions = new String[]{Manifest.permission.READ_EXTERNAL_STORAGE};
ActivityCompat.requestPermissions(this, permissions, MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
}
}else {
//如果已经授权,做读写内存卡操作
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode
, @NonNull String[] permissions
, @NonNull int[] grantResults) {
switch (requestCode){
case MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE :
//如果请求被拒绝 grantResults是空的
if(grantResults.length > 0 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED){
//做读写内存卡的操作
}else {
//请求被拒绝了,提示用户不能进行此操作。
}
}
}
至此,6.0运行时权限适配工作已经完成,虽然挺简单,但整个项目用起来的话,会有很多重复的代码,有需要的同学可以进行相应的封装。现在已经有很多库对申请运行时权限进行封装。下面一个大神封装的公共库。
https://github.com/lovedise/PermissionGen
上面有使用方法,有兴趣的同学可以看看。