Android中运行时权限机制
概述
在Android 6.0以前,我们安装App时会列出安装的App的访问权限,而且只有安装时会出现一次。一旦我们同意并安装了此App,这个App就可以在用户毫不知情的情况下访问权限内的所有东西,比如用户的通信信息,用户的位置等,这会侵犯用户的隐私。
在Android6.0以后,将不会在安装的时候授予权限;取而代之的是,App不得不在运行时一个一个询问用户来授予权限。开发者不能像以前一样随意地调用方法,否则你的App就会崩溃,开发者需要在每个需要权限的地方检查权限,
Normal Permissions与Dangerous Permission
Google将权限分为两类:
一类是Normal Permissions,这类权限一般不涉及用户隐私,是无须用户进行授权的,比如手机振动、访问网络等,这些权限只需要在AndroidManifest.xml中简单声明就好,安装时授权,无须每次使用时都检查权限,而且用户不能取消以上授权;
Normal Permissions如下
android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
android.permission.ACCESS_NETWORK_STATE
android.permission.ACCESS_NOTIFICATION_POLICY
android.permission.ACCESS_WIFI_STATE
android.permission.BLUETOOTH
android.permission.BLUETOOTH_ADMIN
android.permission.BROADCAST_STICKY
android.permission.CHANGE_NETWORK_STATE
android.permission.CHANGE_WIFI_MULTICAST_STATE
android.permission.CHANGE_WIFI_STATE
android.permission.DISABLE_KEYGUARD
android.permission.EXPAND_STATUS_BAR
android.permission.GET_PACKAGE_SIZE
android.permission.INSTALL_SHORTCUT
android.permission.INTERNET
android.permission.KILL_BACKGROUND_PROCESSES
android.permission.MODIFY_AUDIO_SETTINGS
android.permission.NFC
android.permission.READ_SYNC_SETTINGS
android.permission.READ_SYNC_STATS
android.permission.RECEIVE_BOOT_COMPLETED
android.permission.REORDER_TASKS
android.permission.REQUEST_INSTALL_PACKAGES
android.permission.SET_ALARM
android.permission.SET_TIME_ZONE
android.permission.SET_WALLPAPER
android.permission.SET_WALLPAPER_HINTS
android.permission.TRANSMIT_IR
android.permission.UNINSTALL_SHORTCUT
android.permission.USE_FINGERPRINT
android.permission.VIBRATE
android.permission.WAKE_LOCK
android.permission.WRITE_SYNC_SETTINGS
另一类是Dangerous Permissions,一般会涉及用户隐私,需要用户进行授权,比如读取sdcard、访问通信录等。
Dangerous Permissions如下:
group:android.permission-group.CONTACTS
permission:android.permission.WRITE_CONTACTS
permission:android.permission.GET_ACCOUNTS
permission:android.permission.READ_CONTACTS
group:android.permission-group.PHONEpermission: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.permissio
注意:Dangerous Permissions中同一组的任何一个权限被授权了,其他权限也自动被授权。此外,对于申请时弹出的提示框上面的文本说明也是对整个权限组的说明,而不是单个权限的说明。
实现支持运行时权限
通过一个打电话的例子来说明6.0以后如何支持运行时权限
首先我们要在build.gradle中将targetSdkVersion设置为23。
android { compileSdkVersion 23 buildToolsVersion "23.0.3" defaultConfig { applicationId "com.wen.asyl.permissionsdemo" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } }
点击按钮调用打电话,进行打电话之前需要先判断当前App是否有android.permission.CALL_PHONE这个权限。如果有,直接进行打电话的操作,如果没有,需要弹出提示框来进行权限的申请。
public void callOnClick(View view){ //检查app是否有拨打电话的权限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)!= PackageManager.PERMISSION_GRANTED){ //如果没有就进行申请 ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.CALL_PHONE},PERMISSION_REQUEST_CODE); }else { callPhone(); } }
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode==PERMISSION_REQUEST_CODE){ if (grantResults[0]==PackageManager.PERMISSION_GRANTED){ callPhone(); }else{ Toast.makeText(this, "权限被拒绝", Toast.LENGTH_SHORT).show(); } return; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
进行拨打电话
Intent intent=new Intent(Intent.ACTION_CALL); Uri data= Uri.parse("tel:"+"10086"); intent.setData(data); try { startActivity(intent); } catch (Exception e) { e.printStackTrace(); }
如图所示,onRequestPermissionsResult就是申请权限的回调,如果用户选择“允许”,就会进行打电话操作;
如果用户选择“拒绝”,就弹出Toast显示权限被拒绝。
需要注意的是,如果我们选择“允许”,下一次就不会弹出权限申请提示框了;如果我们点击“拒绝”,则下一次还会弹出权限申请提示框,只不过这一次会多出一个选择项,叫做“不再询问”,如果我们勾选了该选项,则下一次就不会弹出权限申请提示框,而直接调用onRequestPermissionsResult,回调结果为最后一次用户的选择,也就是会弹出我们的toast:“权限被拒绝”
如图所示:
处理“不再询问”选项
如果用户选择了“不再询问”,那么我们每次调用需要访问该权限的API都会失效,为了解决这个问题,我们可以使用shouldShowRequestPermissionRational方法。具体实现方法如下:
if (!ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CALL_PHONE)){ AlertDialog dialog=new AlertDialog.Builder(this).setMessage("需要访问电话的权限,不开启将无法使用!").setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(); intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); //设置去向意图 Uri uri = Uri.fromParts("package", MainActivity.this.getPackageName(), null); intent.setData(uri); //发起跳转 startActivity(intent); } }).create(); dialog.show(); } }
我们通过弹出AlertDialog来提醒用户允许访问权限的重要性,点击“确定”的时候会跳到应用开启权限页面,需要用户手动开启权限。如图所示:
小结
在Android6.0以后,权限需手动申请的还有很多,但大部分都是大同小异的,会了一个,基本都差不多理解了。Android中提供了很多框架来简化这个动态添加权限的框架。这种每次需要判断权限很是麻烦,而Android中提供的框架为我们简化了这一操作,我们下一篇博客来讲解一下PermissionsDispatcher框架的使用。
Demo:
https://download.****.net/download/wen_haha/10513915
GitHub: