android踩坑日记:android动态权限分析和解决方案(代码片段)

tuke_tuke tuke_tuke     2022-12-03     758

关键词:

关于运行时权限

 在旧的权限管理系统中,权限仅仅在APP安装时询问一次,用户同意了这些权限App才能安装,APP一旦安装后就可以偷偷做很多不为人知的事情。

我们知道从Android6.0开始,App可以直接安装,在运行时需要用到权限的地方,会一个一个询问用户授予权限,比如照相,写SD卡,读联系人等,系统会弹出一个对话框让用户选择是否授予权限(这个dialog是各个手机厂商定制的,不能由开发者定制),用户可以拒绝,可以在每个APP的权限进行管理

特别注意:这个对话框不是开发者调用某个权限的功能是由系统自动弹出的,而不是由开发者手动调用的。


哪些权限需要动态申请

新的权限策略分为两类:第一类是不涉及用户隐私的,只需要在manifest中声明即可,比如网络,蓝牙,NFC等,第二类是涉及用户隐私信息的,需要用户授权后才可使用的,比如SD卡读写,联系人,短信读写等等

不需要运行时申请的权限:

此类权限都是正常保护的权限,只需要在AndroidManifest.xml中简单声明这些权限即可,安装即授权,不需要每次使用时都检查权限,而且用户不能取消以上授权,除非用户卸载App。

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_IGNORE_BATTERY_OPTIMIZATIONS
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


需要运行时申请的权限

所有危险的Android系统权限属于权限组,如果APP运行在Android 6.0 (API level 23)或者更高级别的设备中,而且targetSdkVersion>=23时,系统将会自动采用动态权限管理策略,如果你在涉及到特殊权限操作时没有申请权限权限而直接调用了相关代码,你的App可能就崩溃了,综上所述你需要注意:

  • 此类权限也必须在Manifest中申明,否则申请时不提示用户,直接回调开发者权限被拒绝。
  • 同一个权限组的任何一个权限被授权了,这个权限组的其他权限也自动被授权。例如一旦WRITE_CONTACTS被授权了,App也有READ_CONTACTS和GET_ACCOUNTS了。
  • 申请某一个权限的时候系统弹出的Dialog是对整个权限组的说明,而不是单个权限。例如我申请READ_EXTERNAL_STORAGE,系统会提示"允许xxx访问设备上的照片、媒体内容和文件吗?

如果App运行在Android 5.1 (API level 22)或者更低级别的设备中,或者targetSdkVersion<=22时(此时设备可以是Android 6.0 (API level 23)或者更高),在所有系统中仍将采用旧的权限管理策略,系统会要求用户在安装的时候授予权限。其次,系统就告诉用户App需要什么权限组,而不是个别的某个权限。

CALENDAR(日历) 
READ_CALENDAR
WRITE_CALENDAR
CAMERA(相机) 
CAMERA
CONTACTS(联系人) 
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATION(位置) 
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONE(麦克风) 
RECORD_AUDIO
PHONE(手机) 
READ_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORS(传感器) 
BODY_SENSORS
SMS(短信) 
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGE(存储卡) 
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE






关于运行时权限的一些建议
  1. 防止一次请求太多的权限或请求次数太多,用户可能对你的应用感到厌烦,在应用启动的时候,最好先请求应用必须的一些权限,非必须权限在使用的时候才请求,建议整理并按照上述分类管理自己的权限:
    1. 普通权限(Normal PNermissions):只需要在Androidmanifest.xml中声明相应的权限,安装即许可
    2. 需要运行时申请的权限(Dangerous Permissions):
      1. 必要权限:最好在应用启动的时候,进行请求许可的一些权限(主要是应用中主要功能需要的权限)。
      2. 附带权限:不是应用主要功能需要的权限(如:选择图片时,需要读取SD卡权限)。
  2. 解释你的应用为什么需要这些权限:在你调用requestPermissions()之前,你为什么需要这个权限
    1. 例如,一个摄影的App可能需要使用定位服务,因为它需要用位置标记照片。一般的用户可能会不理解,他们会困惑为什么他们的App想要知道他的位置。所以在这种情况下,所以你需要在requestpermissions()之前告诉用户你为什么需要这个权限。
  3. 使用兼容库support-v4中的方法
ContextCompat.checkSelfPermission()
ActivityCompat.requestPermissions()
ActivityCompat.shouldShowRequestPermissionRationale()

几个重要的方法与常量解释

  • PackageManager中的两个常量:
    • PackageManager.PERMISSION_DENIED:该权限是被拒绝的。
    • PackageManager.PERMISSION_GRANTED:该权限是被授权的。
  • Activity中或者Fragment都会有以下几个方法:

int checkSelfPermission(String)
void requestPermissions(int, String...)
boolean shouldShowRequestPermissionRationale(String)
void onRequestPermissionsResult()
checkSelfPermission() 检查权限

  1. 检查某一个权限的当前状态,你应该在请求某个权限时检查这个权限是否已经被用户授权,已经授权的权限重复申请可能会让用户产生厌烦。
  2. 该方法有一个参数是权限名称,有一个int的返回值,用这个值与上面提到的两个常量做比较可判断检查的权限当前的状态。
if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) 
    // 没有权限,申请权限。
else
    // 有权限了,去放肆吧。
r equestPermissions() 申请权限
  1. 请求用户授权几个权限,调用后系统会显示一个请求用户授权的提示对话框,App不能配置和修改这个对话框,如果需要提示用户这个权限相关的信息或说明,需要在调用 requestPermissions() 之前处理,该方法有两个参数: 
    1. int requestCode,会在回调onRequestPermissionsResult()时返回,用来判断是哪个授权申请的回调。
    2. String[] permissions,权限数组,你需要申请的的权限的数组。
  2. 由于该方法是异步的,所以无返回值,当用户处理完授权操作时,会回调Activity或者Fragment的onRequestPermissionsResult()方法。
对于Activity我们直接调用requestPermissions(int, String[])即可,不过这个方法是在api leve 23以上,所以我们为了适配可以是使用兼容包提供的方法:
ActivityCompat.requestPermissions(activity, new String[]Manifest.permission.READ_CONTACTS, MMM);
onRequestPermissionsResult() 处理权限结果回调
  1. 该方法在Activity/Fragment中应该被重写,当用户处理完授权操作时,系统会自动回调该方法,该方法有三个参数: 
    1. int requestCode,在调用requestPermissions()时的第一个参数。
    2. String[] permissions,权限数组,在调用requestPermissions()时的第二个参数。
    3. int[] grantResults,授权结果数组,对应permissions,具体值和上方提到的PackageManager中的两个常量做比较。
@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) 
    switch (requestCode) 
        case MMM: 
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                // 权限被用户同意,可以去放肆了。
             else 
                // 权限被用户拒绝了,洗洗睡吧。
            
            return;
        
    
shouldShowRequestPermissionRationale()
  1. 望文生义,是否应该显示请求权限的说明。
  2. 第一次请求权限时,用户拒绝了,调用shouldShowRequestPermissionRationale()后返回true,应该显示一些为什么需要这个权限的说明。
  3. 用户在第一次拒绝某个权限后,下次再次申请时,授权的dialog中将会出现“不再提醒”选项,一旦选中勾选了,那么下次申请将不会提示用户。
  4. 第二次请求权限时,用户拒绝了,并选择了“不再提醒”的选项,调用shouldShowRequestPermissionRationale()后返回false。
  5. 设备的策略禁止当前应用获取这个权限的授权:shouldShowRequestPermissionRationale()返回false 。
  6. 加这个提醒的好处在于,用户拒绝过一次权限后我们再次申请时可以提醒该权限的重要性,免得再次申请时用户勾选“不再提醒”并决绝,导致下次申请权限直接失败。
f (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) 
    // 没有权限。
    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) 
            // 用户拒绝过这个权限了,应该提示用户,为什么需要这个权限。
     else 
        // 申请授权。
        ActivityCompat.requestPermissions(thisActivity, new String[]Manifest.permission.READ_CONTACTS, MMM);
    


...

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) 
    switch (requestCode) 
        case MMM: 
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                // 权限被用户同意,可以去放肆了。
             else 
                // 权限被用户拒绝了,洗洗睡吧。
            
            return;
        
    

从上面来看,判断很多,逻辑也很多,这样就加重了我们开发的负担,加上很多人反馈说国产手机有各种各样的bug,这样兼容起来就更加麻烦了,那么下面我就为大家介绍一个开源内裤来解决这一系列问题。

说说Android6.0以上不申请权限直接调用相机的情况: 1,对于国外的手机比如三星,不询问权限直接调用,会直接崩溃,这符合android原生系统的权限管理策略 2,对于国内手机,比如vivo,不询问相机权限,照样会给出弹窗,而且即使勾选“不在提醒”且拒绝权限,下次调用还是照样弹窗直到用户授权才能用。这应该是vivo系统定制的,系统帮助应用询问的权限 3,对于国内手机,魅族系列,不询问相机权限,依然可以使用相机,猜测是系统给默认授权了。



AndPermission
这个开源库名叫AndPermission:https://github.com/yanzhenjie/AndPermission,经过我的实践是完全解决了上述问题,推荐大家使用。
Gradle
compile 'com.yanzhenjie:permission:1.0.6'
使用介绍
我建议看官去Github下载Demo并阅读本文会帮助你理解 https://github.com/yanzhenjie/AndPermission



android踩坑日记:android7.0动态相机权限(代码片段)

...jie1003添加依赖:compile'com.yanzhenjie:permission:1.0.3'Android6.0:众所周知,Android6.0时相机摄像头权限改成了动态权限申请。实际上在xml中加入CAMERA,WRITE_EXTERNAL_STORAGE全向后,直接调用摄像头。此时是没有“检查权限是否... 查看详情

android踩坑日记:点击变暗效果的imageview实现原理

很多时候我们使用ImagView显示图片,无论是Gilde,Fresco等图片显示框架,比如设置中心更换头像,网格相册点击预览,选择等情况,会遇到点击变暗的交互需求。源码分析    我们想的办法是自定义一个Ima... 查看详情

android踩坑日记:android字体属性及测量(fontmetrics)

Android字体属性及测量(FontMetrics)字体的几个参数,以AndroidAPI文档定义为尊,见下图要点如下:基准点是baselineAscent是baseline之上至字符最高处的距离Descent是baseline之下至字符最低处的距离Leading文档说的很含... 查看详情

android踩坑日记:floatingactionbutton的设置大小问题

FloatingActionButton(FAB)是Android5.0新特性——MaterialDesign中的一个控件,是一种悬浮的按钮。FloatingActionButton是ImageView的子类,因此它具备ImageView的全部属性基本使用1,需要在gradle文件中先注册依赖:compile 查看详情

100个unity踩坑小知识点|unity调用api,动态获取android权限,附带所有android权限表格(代码片段)

...戏!Unity踩坑小知识点学习Unity调用API,动态获取Android权限在开发安卓应用时,经常会用到获取Android权限的问题。这个问题有两种解决方案一种是在Android端就已经写好了动态获取权限的方法,Unity调用该方法就可... 查看详情

android踩坑日记:自定义水平和圆形progressbar样式

自定义水平和圆形ProgressBar样式1.自定义水平ProgressBar样式ProgressBar分为两种,我们能明确看到进度,不确定的就是不清楚、不确定一个操作需要多长时间来完成,这个时候就需要用的不确定的ProgressBar了。ProgressBar࿰... 查看详情

android踩坑日记:监听软键盘多次调用和刷新系统相册和获取所有相片

EditText设置监听软键盘删除键(或enter)一般使用方法edittext.setOnKeyListener(newView.OnKeyListener()@OverridepublicbooleanonKey(Viewv,intkeyCode,KeyEventevent)if(keyCode==KeyEvent.KEYCODE_DEL)//处理操作);问题ÿ 查看详情

android踩坑日记:floatingactionbutton的设置大小问题

FloatingActionButton(FAB)是Android5.0新特性——MaterialDesign中的一个控件,是一种悬浮的按钮。FloatingActionButton是ImageView的子类,因此它具备ImageView的全部属性基本使用1,需要在gradle文件中先注册依赖:compile... 查看详情

android踩坑日记:点击变暗效果的imageview实现原理

很多时候我们使用ImagView显示图片,无论是Gilde,Fresco等图片显示框架,比如设置中心更换头像,网格相册点击预览,选择等情况,会遇到点击变暗的交互需求。源码分析    我们想的办法是自定义一个Ima... 查看详情

android踩坑日记:使用fesco图片加载库在gridview上的卡顿优化

1,fresco是一个强大的图片加载库2,fresco设计了一个叫做imagepipeline(图片管道)的模块,它负责从从网络,从本地文件系统,从本地资源加载图片,为了最大限度节约资源和cpu时间,它设计了3... 查看详情

android踩坑日记:okhttp设置user-agent你可能没遇到的坑

Okhttp设置User-Agent你可能没遇到的坑HttpHeader之User-Agent   User-Agent中文名为用户代理,是Http协议中的一部分,属于头域的组成部分,UserAgent页简称UA。她是一个特殊字符串头,是一种想访问网站提供你说使用的浏览... 查看详情

android踩坑小记:在onresume中申请权限(代码片段)

这里写自定义目录标题背景:不停弹出的Toast定位过程根本原因(转自下面相关链接)后续优化相关链接背景:不停弹出的Toast我们在某个页面的onResume中进行了权限的判断和申请,在权限拒绝的回调onRequestPermi... 查看详情

android踩坑日记:监听软键盘多次调用和刷新系统相册和获取所有相片

EditText设置监听软键盘删除键(或enter)一般使用方法edittext.setOnKeyListener(newView.OnKeyListener()@OverridepublicbooleanonKey(Viewv,intkeyCode,KeyEventevent)if(keyCode==KeyEvent.KEYCODE_DEL)//处理操作);问题:当点击软键盘的删除键时,处理操... 查看详情

android踩坑日记:recyclerview中edittext和imageview的viewholder复用坑

RecyclerView中EditText和ImageView的ViewHolder复用坑RecyclerView作为ListView的升级版,目前来讲讲开发过程遇到的坑。RecyclerView中使用EditText滚动后数据消失,错乱场景:RecyclerView中的每个Item的ViewHolder布局中为都有EditText控件... 查看详情

android安全与隐私相关特性的行为变更分析(代码片段)

文章目录前言Android131.1新增通知运行时权限1.2新增WIFI运行时权限1.3精细化媒体访问权限1.4Intent调用组件过滤1.5动态广播接收器限制1.6后台访问身体传感器Android122.1剪贴板内容访问提醒2.2软件包可见性的限制2.3限制悬浮窗点击透... 查看详情

android安全与隐私相关特性的行为变更分析(代码片段)

文章目录前言Android131.1新增通知运行时权限1.2新增WIFI运行时权限1.3精细化媒体访问权限1.4Intent调用组件过滤1.5动态广播接收器限制1.6后台访问身体传感器Android122.1剪贴板内容访问提醒2.2软件包可见性的限制2.3限制悬浮窗点击透... 查看详情

android安全与隐私相关特性的行为变更分析(代码片段)

文章目录前言Android131.1新增通知运行时权限1.2新增WIFI运行时权限1.3精细化媒体访问权限1.4Intent调用组件过滤1.5动态广播接收器限制1.6后台访问身体传感器Android122.1剪贴板内容访问提醒2.2软件包可见性的限制2.3限制悬浮窗点击透... 查看详情

android安全与隐私相关特性的行为变更分析(代码片段)

文章目录前言Android131.1新增通知运行时权限1.2新增WIFI运行时权限1.3精细化媒体访问权限1.4Intent调用组件过滤1.5动态广播接收器限制1.6后台访问身体传感器Android122.1剪贴板内容访问提醒2.2软件包可见性的限制2.3限制悬浮窗点击透... 查看详情