Android的权限管理遵循的是“最小特权原则”,即所有的Android应用程序都被赋予了最小权限。一个Android应用程序如果没有声明任何权限,就没有任何特权。
权限的历史
2013年,苹果公司发布IOS7系统。其中一项令开发者头疼的修改点:隐私中增加相册、录音等权限,App如需使用相应权限,需要申请并由用户同意(IOS7以前,可以直接访问相册),针对此点,很多App在首次启动时一通弹窗,申请各式各样的权限。
后来苹果为改善用户体验,在App
Store审核时要求App必须在使用前一刻才能申请权限,有效改善了此类问题。比如一款直播App,当你启动App时并不需要相机、录音权限,等到你开播时才需要申请这两个权限。这一场景,其实就类似今天要提到的Android动态授权。
早期:无遮拦
Android6.0系统以前,在安装App前,会罗列出App申请的所有权限。如果继续安装,视为用户同意赋予App所需权限。这种机制是无遮拦的,在尝试安装App时,弹窗罗列了App申请的全部权限。只能对所需权限进行查看,无法拒绝授权,可选择取消安装或继续安装。
这种方式,对于开发者极为友好,仅需在Manifest中配置App所需权限即可,代码就可以直接调用了。但是对于用户来说,这种方法存在极大的安全隐患。
发展:第三方安全App
为解决部分敏感权限被不合理使用,国内部分公司的安全类App,开始监控应用获取手机敏感权限并做出提示。如360手机卫士、腾讯手机管家等产品,当监测到有App尝试使用短信权限、定位等敏感权限,会告知用户,并可以拒绝赋予权限。刚开始,还比较顺利。但随着手机厂商逐渐开始修改ROM,第三方安全App的兼容、性能问题逐步爆发。
升级:厂商行动
再稍后一些,手机厂商开始行动,纷纷将第三方软件的权限提示功能直接做入ROM。并把安全作为产品的卖点进行打造。
目前:谷歌升级权限管理
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、已授权的情况下,执行相应的操作
当已授权时,就可以执行原有的操作了。代码如下:
// 检测PHONE_STATE 如果已授权 if
(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED) { //做你想做的 }
3、未授权时,申请权限
如果App未获得授权,我们就需要向用户申请授权。可以调用requestPermissions()方法来请求授权。代码如下:
// 检测PHONE_STATE 如果未授权 if
(ContextCompat.checkSelfPermission(this,Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) { //申请权限
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.READ_PHONE_STATE), PERMISSIONS_REQUEST_PHONE_STATE)
}
requestPermissions()中的第三个参数是一个int型请求码,方便回调处理。
调用申请授权方法后,ROM会调起一个系统级弹窗,这个dialog开发者无法定制。当用户点击同意后,系统会记录,下次再判断权限时就会返回已授权状态;当App卸载时,记录会被清除。
4、重写函数,处理授权弹窗的结果
直接在Activity或Fragment中重写onRequestPermissionsResult()函数,来处理权限申请结果。requestPermissions()的第三个参数,将在这里被用到。代码如下:
public void onRequestPermissionsResult(int requestCode, @NonNull String[]
permissions, @NonNull int[] grantResults) { if (requestCode ==
PERMISSIONS_REQUEST_READ_CONTACTS) { if (grantResults[0] ==
PackageManager.PERMISSION_GRANTED) { //todo } else { //todo } } }