Android的权限管理遵循的是“*小特权原则”,即所有的Android应用程序都被赋予了*小权限。一个Android应用程序如果没有声明任何权限,就没有任何特权。

权限的历史

2013年,苹果公司发布IOS7系统。其中一项令开发者头疼的修改点:隐私中增加相册、录音等权限,App如需使用相应权限,需要申请并由用户同意(IOS7以前,可以直接访问相册),针对此点,很多App在首次启动时一通弹窗,申请各式各样的权限。

后来苹果为改善用户体验,在App Store审核时要求App必须在使用前一刻才能申请权限,有效改善了此类问题。比如一款直播App,当你启动App时并不需要相机、录音权限,等到你开播时才需要申请这两个权限。这一场景,其实就类似今天要提到的Android动态授权。

早期:无遮拦

Android6.0系统以前,在安装App前,会罗列出App申请的所有权限。如果继续安装,视为用户同意赋予App所需权限。这种机制是无遮拦的,在尝试安装App时,弹窗罗列了App申请的全部权限。只能对所需权限进行查看,无法拒*授权,可选择取消安装或继续安装。

这种方式,对于开发者*为友好,仅需在Manifest中配置App所需权限即可,代码就可以直接调用了。但是对于用户来说,这种方法存在*大的安全隐患。

%title插图%num

发展:第三方安全App

为解决部分敏感权限被不合理使用,国内部分公司的安全类App,开始监控应用获取手机敏感权限并做出提示。如360手机卫士、腾讯手机管家等产品,当监测到有App尝试使用短信权限、定位等敏感权限,会告知用户,并可以拒*赋予权限。刚开始,还比较顺利。但随着手机厂商逐渐开始修改ROM,第三方安全App的兼容、性能问题逐步爆发。

升级:厂商行动

再稍后一些,手机厂商开始行动,纷纷将第三方软件的权限提示功能直接做入ROM。并把安全作为产品的卖点进行打造。

%title插图%num

 

目前:谷歌升级权限管理

2015年推出的Android 6.0,加入了危险权限管理。因手机厂商对ROM的修改,部分6.0以上机器并不支持此项特性。到了此阶段,App需要在对权限代码进行修改后,才能正常使用对应权限。简单理解为3步:

  • 1、判断是否授权;
  • 2、如果未授权需申请权限,根据授权结果继续执行;
  • 3、已授权可以继续操作。

权限的使用和适配

零、权限的基础知识

Android系统基于Linux内核,系统中的权限分为3类:

  • Android手机所有者权限:这个和厂商相关,可以理解为系统权限。
  • Android ROOT权限:类似于Linux,这是Android系统中的*高权限。如果拥有该权限,就可以对Android系统中的任何文件、数据、资源进行任意操作。所谓“越狱”,就是令用户获得*高的ROOT权限。
  • Android应用程序权限:该权限在AndroidManifest文件中由程序开发者声明,在需要时由用户授权。

一、不适用动态权限

动态权限特性,仅从Android 6.0开始拥有,所以,可以简单粗暴的通过不提升targetSDK(targetSDK<23)的方式,便可不触发此特性。

如果不改变任何代码,直接将targetSDK提升到26,然后运行App,做同样操作时会发生异常甚至崩溃,产生这个崩溃的原因,是在Android 6.0及以上,未获取权限的情况下直接执行了需要权限的操作。

二、动态权限适配

1、在使用前权限前,检测权限

首先,我们需要判断自己是否拥有权限。判断时间点为执行需要权限的对应操作前。如我们在获取IMEI前,需要判断是否拥有PHONE_STATE权限。

我们可以调用ContextCompat.checkSelfPermission()方法检测授权状态,返回的结果为PackageManager中的两个常量:PERMISSION_GRANTED(已授权)和PERMISSION_DENIED(未授权)。

2、已授权的情况下,执行相应的操作

当已授权时,就可以执行原有的操作了。代码如下:

  1. // 检测PHONE_STATE 如果已授权
  2. if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
  3. //做你想做的
  4. }

3、未授权时,申请权限

如果App未获得授权,我们就需要向用户申请授权。可以调用requestPermissions()方法来请求授权。代码如下:

  1. // 检测PHONE_STATE 如果未授权
  2. if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
  3. //申请权限
  4. ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), PERMISSIONS_REQUEST_PHONE_STATE)
  5. }

requestPermissions()中的第三个参数是一个int型请求码,方便回调处理。

调用申请授权方法后,ROM会调起一个系统级弹窗,这个dialog开发者无法定制。当用户点击同意后,系统会记录,下次再判断权限时就会返回已授权状态;当App卸载时,记录会被清除。

4、重写函数,处理授权弹窗的结果

直接在Activity或Fragment中重写onRequestPermissionsResult()函数,来处理权限申请结果。requestPermissions()的第三个参数,将在这里被用到。代码如下:

  1. public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
  2. if (requestCode == PERMISSIONS_REQUEST_READ_CONTACTS) {
  3. if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
  4. //todo
  5. } else {
  6. //todo
  7. }
  8. }
  9. }