Android--权限管理
来源:互联网 发布:都怪本泽马什么梗 知乎 编辑:程序博客网 时间:2024/06/07 02:52
权限机制
对于6.0以下的权限在安装的时候,根据权限声明产生一个权限列表,用户只有在同意之后才能完成app的安装,造成了我们想要使用某个app,就要忍受其一些不必要的权限。
Android 6.0推出了新的权限机制,我们可以直接安装,当app需要我们授予不恰当的权限的时候,我们可以予以拒绝。当然我们也可以在设置界面对每个app的权限进行查看,以及对单个权限进行授权或者解除授权。
Google将权限分为两类,一类是Normal Permissions,这类权限一般不涉及用户隐私,是不需要用户进行授权的,比如手机震动、访问网络等;另一类是Dangerous Permission,一般是涉及到用户隐私的,需要用户进行授权,比如读取sdcard、访问通讯录等,新的权限机制更好的保护了用户的隐私。
Normal Permission:
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
我们可以用 adb 命令查看危险的权限,Dangerous Permission:
adb shell pm list permissions -d -gDangerous Permissions: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_VOICEgroup:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDARgroup:android.permission-group.CAMERA permission:android.permission.CAMERAgroup:android.permission-group.SENSORS permission:android.permission.BODY_SENSORSgroup:android.permission-group.SUPERUSER permission:android.permission.ACCESS_SUPERUSERgroup: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_AUDIOgroup: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_BROADCASTSungrouped:
看到上面的dangerous permissions,我们会发现危险权限都是以组来分的。那么分组会有什么影响。
如果app运行在Android 6.0 或以上的机器上,对于授权机制是这样的。
如果你申请某个危险的权限,假设你的app早已被用户授权了同一组的某个危险权限,那么系统会立即授权,而不需要用户去点击授权。比如你的app对READ_CONTACTS已经授权了,当你的app申请WRITE_CONTACTS时,系统会直接授权通过。
此外,对于申请时弹出的dialog上面的文本说明也是对整个权限组的说明,而不是单个权限(这个dialog是不能进行定制的)。
不过需要注意的是,不要对权限组过多的依赖,尽可能对每个危险权限都进行正常流程的申请,因为在后期的版本中这个权限组可能会产生变化。
API处理
新增的 API 有如下几个:
ContextCompat.checkSelfPermissionActivityCompat.requestPermissions()onRequestPermissionsResult()ActivityCompat.shouldShowRequestPermissionRationale
checkSelfPermission 是检查我们是否已申请了某个权限,如果没有申请就调用 requestPermissions() 来进行权限申请。另外因为 ActivityCompat 继承了 ContextCompat,所以我们都用 ActivityCompat 也是可以的。
因为申请权限是个异步的过程,所以我们要在一个回调里去处理结果,就是 onRequestPermissionsResult(),我们在这里拿到用户是否通过了授权,通过就去做通过的处理,失败就做提示处理。
shouldShowRequestPermissionRationale 是告诉用户这个权限是干什么的,只有拒绝授予权限之后再弹出权限框才会出现,你需要给用户一个解释。
使用流程:
在AndroidManifest文件中添加需要的权限。
这个步骤和我们之前的开发并没有什么变化,试图去申请一个没有声明的权限可能会导致程序崩溃。
检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {}else{ //}
checkSelfPermission 方法返回值为PackageManager.PERMISSION_DENIED或者PackageManager.PERMISSION_GRANTED。当返回DENIED就需要进行申请授权了。
权限申请
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS);}else{ //}
该方法是异步的,第一个参数是Context;第二个参数是需要申请的权限的字符串数组;第三个参数为requestCode,主要用于回调的时候检测。可以从方法名requestPermissions以及第二个参数看出,是支持一次性申请多个权限的,系统会通过对话框逐一询问用户是否授权。
权限申请回调
@Overridepublic void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_READ_CONTACTS: { // 如果请求被拒绝了,这个结果数组是空的 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 同意了,做你需要做的与该权限相关的任务。 } else { //许可拒绝,禁用依赖于此权限的功能。 } return; }}}
对于权限的申请结果,首先验证 requestCode 定位到我们的申请,然后验证 grantResults 对应于申请的结果,这里的数组对应于申请时的第二个权限字符串数组。如果我们同时申请两个权限,那么 grantResults 的 length 就为2,分别记录我们两个权限的申请结果。如果申请成功,就可以做我们的事情了。
步骤就是这样了,为了用户体验,我们还要把 shouldShowRequestPermissionRationale 向用户解释加进去。
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) { // 我们是否应该显示解释 if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity, Manifest.permission.READ_CONTACTS)) { // 给个解释给用户,不异步阻塞这个线程等待用户的响应!用户看到说明后,再试一次请求权限。 } else { // 不需要解释,我们可以请求许可。 ActivityCompat.requestPermissions(thisActivity, new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS); // MY_PERMISSIONS_REQUEST_READ_CONTACTS 是一个我们定义的int常数。回调方法获取请求的结果,我们可以给它赋值为0为1都可以。 }}
封装
虽然权限申请并不复杂,但是重复的代码太多,我们对权限的操作并不是这个我们要开发 app 的要点,如果把它混杂在我们的 MainActivity 里会影响代码的可读性。所以在对权限的操作比较多的时候,我们最好对其进行封装。
原本我们的申请代码是这样的:
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CALL_PHONE}, MY_PERMISSIONS_REQUEST_CALL_PHONE);} else{ //}
可以看到不同权限的逻辑是相同的,不同的只是参数。通过上面的代码,我们可以知道需要的是这三个参数:
- Activity | Fragment
- permissions(权限字符串数组)
- requestCode(int型申请码)
所以我们只要写个方法来接收这三个参数即可,所有权限逻辑都相同。通常我们都会把公共的方法都写在一个 BaseActivity 里,然后让我们的 MainActivity 去继承它,在这里我也这样去做。
BaseActivity.java:
public class BaseActivity extends AppCompatActivity { protected void isPermissionGranted(String permission, int requestCode) { if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M){ return ; } //判断是否需要请求允许权限 if (hasPermission(new String[]{permission})) { requestPermissions(new String[]{permission}, requestCode); } } protected void isPermissionAllGranted(String[] permissions, int requestCode) { if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M){ return ; } //获得批量请求但被禁止的权限列表 List<String> deniedPerms = new ArrayList<String>(); for(int i = 0; permissions != null && i < permissions.length;i++){ if (!hasPermission(new String[]{permissions[i]})) { deniedPerms.add(permissions[i]); } } //进行批量请求 int denyPermNum = deniedPerms.size(); if(denyPermNum != 0){ requestPermissions(deniedPerms.toArray(new String[denyPermNum]), requestCode); } } /** * 为子类提供一个权限检查方法 * @param permissions * @return */ public boolean hasPermission(String[] permissions) { for (String permission: permissions) { if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } /** * 为子类提供一个权限请求方法 * @param requestCode * @param permissions */ public void requestPermission(int requestCode, String[] permissions) { ActivityCompat.requestPermissions(this, permissions, requestCode); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if(grantResults.length == 0){ return; } switch (requestCode) { case 1 : if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "未授予拨打电话权限", Toast.LENGTH_SHORT).show(); }else{ doCallPhone(); } break; case 2 : if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "未授予读取SD卡权限", Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(this, "用户已经授予读取SD卡权限", Toast.LENGTH_SHORT).show(); } break; case 3 : for (int i = 0; i < grantResults.length; i++) { if (grantResults[i] != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "有权限未授予,部分功能不能使用", Toast.LENGTH_SHORT).show(); break; } else if (i == grantResults.length - 1){ Toast.makeText(this, "用户已经授予全部权限", Toast.LENGTH_SHORT).show(); } } break; } super.onRequestPermissionsResult(requestCode, permissions, grantResults); } private void doCallPhone() { Intent intent = new Intent(Intent.ACTION_CALL); Uri data = Uri.parse("tel:" + "10086"); intent.setData(data); startActivity(intent); }}
MainActivity.java:
public class MainActivity extends BaseActivity { private Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); isPermissionAllGranted(new String[]{Manifest.permission.CALL_PHONE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 3); mButton = (Button) findViewById(R.id.setting); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //前往应用详情界面 try { Uri packUri = Uri.parse("package:"+getPackageName()); Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS,packUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); } catch (Exception e) { } } }); }}
这里我做了一个小例子,申请了读取SD卡和拨打电话两个权限,可做批量申请,并且回调方法可进入拨号界面,如果 API < 23就不做申请,因为是6.0以下。因为这是举例,我没有创建常量类,拨打电话的 requestCode 是1,SD卡是2,批量是3。代码很简单,稍注意的是在批量申请时有 List 集合记录未申请的权限。
界面上有一个 Button,点击可进入系统的设置,可手动更改权限。
如果是 Fragment,我们只要用getActivity()方法获得 Activity 对象,通过这个对象调用checkSelfPermission相关方法就可以啦。
public Activity getActivity(Object object) { if (object instanceof Activity) { return (Activity) object; } else if (object instanceof Fragment) { return ((Fragment) object).getActivity(); } return null; }
结束语:本文仅用来学习记录,参考查阅。
- android权限管理大全
- Android权限管理
- Android应用程序权限管理
- android权限管理
- Android 读取权限管理
- Android权限管理
- android 6.0权限管理
- Android 6.0权限管理
- Android 6.0权限管理
- android 6.0权限管理
- Android M权限管理
- 吐槽 Android 权限管理
- Android Studio权限管理
- Android 6.0 权限管理
- Android应用程序权限管理
- Android 6.0 权限管理
- Android 权限管理
- Android 6.0 权限管理
- 关于java异常你需要初步了解的东西
- ios上架应用在苹果商店搜不到
- LeetCode-230. Kth Smallest Element in a BST (JAVA)二叉树第k小的数字
- Android学习第十篇;MonkeyRunner的使用
- 约瑟夫环
- Android--权限管理
- 史上最简单的 MySQL 教程(五)「SQL 基本操作 之 表操作」
- 基于Portlet的调度任务
- Android开发中libs包下面的mips、armeabi、armeabi-v7a和x86
- sql 锁类型与锁机制
- 项目压力测试 出分析报告 性能调优
- laravel学习笔记(1)— Laravel的安装及laravel中的路由介绍
- 496. Next Greater Element I
- jMeter的安装