Android 6.0(API 23)后的权限问题

一、Android 6.0后的权限问题

谷歌发布的Android 6.0,与之前的版本相比,有了很多的亮点。诸如App Permissions(软件权限管理)、Chrome Custom Tabs(Chrome的网页浏览体验提升)、App Links(APP关联)、Android Pay(安卓支付)、Fingerprint Support(指纹支持)、Power & Change(电量管理 )等等。但是,我们今天要说的是App Permissions,没错,就是它。

相信在Android 6.0之前,安卓开发者可以为所欲为地在App中获取用户的隐私资料,而且是在你不知情的情况下。这就引发了很多对隐私安全的争论,谷歌也知道了这个问题,本着用户至上的原则,在Android 6.0中,Android系统加入了软件管理权限。用户用起来是放心了很多,但对于开发者,说多都是泪,当然,只是要在编程的过程中做多一些工作而已。

在Android中,如果你在App中的AndroidManifest.xml中没有声明软件的权限,你的App就没权限去做相应的事,这是众所周知的。但是在Android 6.0后,有些权限即使你在AndroidManifest.xml文件中添加了声明,也还是会报java.lang.SecurityException异常。因为这个时候你很可能申请了Dangerous Permissions(危险权限),何为危险权限,下面会进行说明。

二、权限的分类

按照谷歌API Guides的说法,系统权限分类几个保护

1. Normal permissions

从字面翻译过来就是普通权限,但是API Guides中的中文版中译成了正常权限,这翻译有点生硬了些,为了方便理解,以下称为普通权限。
引用官方对Normal permissions的解释:

Normal permissions cover areas where your app needs to access data or resources outside the app’s sandbox, but where there’s very little risk to the user’s privacy or the operation of other apps. For example, permission to set the time zone is a normal permission. If an app declares that it needs a normal permission, the system automatically grants the permission to the app. For a full listing of the current normal permissions, see Normal permissions Normal permissions.

也就是说,普通权限包括应用需要访问其沙盒(sandbox)外部数据或资源,也就是对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是普通权限,如果应用声明了其需要这些权限(其实也就是在AndroidManifest.xml中添加了声明),那么在使用中,系统会自动向应用授予该权限,并且用户无法撤销这些权限。

普通的权限有很多,以下为普通权限表:

  • 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

2. Dangerous permissions

Dangerous permissions cover areas where the app wants data or resources that involve the user’s private information, or could potentially affect the user’s stored data or the operation of other apps. For example, the ability to read the user’s contacts is a dangerous permission. If an app declares that it needs a dangerous permission, the user has to explicitly grant the permission to the app.

翻译过来也就是,危险权限包括应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,读取用户的联系人的权限就属于危险权限。如果应用声明需要使用危险权限,就要那么用户就必须明确向该应用授予权限,否则应用就不能违背用户意愿去执行“危险操作”。

当然,危险权限的说法只在设备运行的是Android 6.0(API级别23)以上,并且应用的targetSdkVersion是23或更高的版本才有效。换句话说,如果你的设备是Android 6.0以下或者你使用的是targetSdkVersion在23以下的App,那么你的隐私将有可能被轻易的泄露。当然,Android 5.1(API级别22)或更低的版本,系统在安装时就会要求用户授予权限。

If an app requests a dangerous permission listed in its manifest, and the app does not currently have any permissions in the permission group, the system shows a dialog box to the user describing the permission group that the app wants access to. The dialog box does not describe the specific permission within that group. For example, if an app requests the READ_CONTACTS permission, the system dialog box just says the app needs access to the device’s contacts. If the user grants approval, the system gives the app just the permission it requested.

如果应用请求了AndroidManifest.xml列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组,对话框不描述组内的具体权限。例如,如果应用请求READ_CONTACTS 权限,系统对话框只是说明应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。这一点我觉得我们国内的ROM厂商做的很好,在申请例如SEND_SMS 权限的时候,弹出的对话框提示的是该应用正在尝试发送短信。

如果应用请求其AndroidManifest.xml中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,无需与用户进行交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。

以下为危险权限和权限组

3.Special Permissions

顾名思义,就是特殊权限。例如SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS,这些权限的行为方式和普通权限和危险权限都不同,特别敏感。因此大多数应用都不应该使用这些特殊权限。如果应用实在是需要使用这些特殊权限时,必须先在AndroidManifest.xml文件中声明,然后发送用户授权的Intent,那么系统就会向用户显示详细的管理屏,以响应这些Intent

三、动态申请权限

1. 配置权限

要使用权限,就要现在AndroidManifest.xml中先声明权限才行

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.bisondev.myframework">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

2.判断系统版本

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) //只有当系统是Android6.0以上时,才需要动态申请权限

3.判断是否申请了权限

int permissionStatus = ActivityCompat.checkSelfPermission(SMSActivity.this,Manifest.permission.SEND_SMS);

此时permissionStatus有两种取值

  • PackageManager.PERMISSION_GRANTED//已经授权给这个包了,接下来你可以执行你的业务逻辑代码
  • PackageManager.PERMISSION_DENIED//还没有授权给这个包,你要往下动态申请权限

4.判断是否可以通过弹窗解释授权

boolean hasPermission = ActivityCompat.shouldShowRequestPermissionRationale(SMSActivity.this,Manifest.permission.SEND_SMS);

当Build.VERSION.SDK_INT>=23时,才有动态权限,才需要弹窗去申请,所以当Build.VERSION.SDK_INT < 23时,返回的是false;这里可以从源码中看出来

/*
 * @param activity The target activity.
 * @param permission A permission your app wants to request.
 * @return Whether you can show permission rationale UI.
 *
 * @see #checkSelfPermission(android.content.Context, String)
 * @see #requestPermissions(android.app.Activity, String[], int)
 */
public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
        @NonNull String permission) {
    if (Build.VERSION.SDK_INT >= 23) {
        return ActivityCompatApi23.shouldShowRequestPermissionRationale(activity, permission);
    }
    return false;
}

这里可以看出,当你的Build.VERSION.SDK_INT小于23时,方法直接返回false,此时你可以直接请求申请任何需要的权限。如果用户以前拒绝了一个请求,那么这个方法将会是true。也就是说,用户第一次安装,这个方法返回的是false,只有当用户拒绝过,并且没有按不再提示该窗口之后,这个方法将会返回true。这个时候,可能用户不明白你是否需要这个权限,需要你对利用此权限进行解释说明。

5.申请权限

ActivityCompat.requestPermissions(SMSActivity.this, permission,0);

6.对申请权限结果进行处理

在onRequestPermissionsResult()方法中,可以对申请结果进行相应的处理

    /**
     * 处理申请权限的结果
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode) {
            case 0:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED){
                    //执行逻辑代码
                    sentMessage();
                }else {
                    //没有拿到授权后的处理代码
                }

                break;
        }
    }

四、Fragment种的动态申请权限

和Activity的动态申请大体相似,只是第4和第5步有所不同,在Fragment中申请权限,不要使用ActivityCompat.requestPermissions,直接使用Fragment的requestPermissions方法就行,否则会回调到Activity的onRequestPermissionsResult方法。

在Fragment中嵌套Fragment,在子Fragment中使用requestPermissions方法,不会回调父Fragment的onRequestPermissionsResult。不过建议使用getParentFragment().requestPermissions方法,把回调回传给父Fragment的onRequestPermissionsResult方法。

 @Override
  public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
      super.onRequestPermissionsResult(requestCode, permissions, grantResults);
      List<Fragment> fragments = getChildFragmentManager().getFragments();
      if (fragments != null) {
          for (Fragment fragment : fragments) {
              if (fragment != null) {
                  fragment.onRequestPermissionsResult(requestCode,permissions,grantResults);
              }
          }
      }
  }

这样就可以在父Fragment中把请求结果透传到子Fragment中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是的,如果你已经在 AndroidManifest.xml 文件中添加了 ACCESS_FINE_LOCATION 权限声明,但仍然出现该错误,那么你需要在代码中请求用户授权。Android 6.0API 级别 23)或更高版本中引入了运行时权限系统,它允许应用程序在运行时请求用户授权,以便访问敏感的设备功能。 以下是一些示例代码,它演示了如何在应用程序中请求 ACCESS_FINE_LOCATION 权限: ```java // 检查是否已经授予 ACCESS_FINE_LOCATION 权限 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // 如果未授予该权限,则请求用户授权 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, MY_PERMISSIONS_REQUEST_LOCATION); } // 处理用户授权的结果 @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == MY_PERMISSIONS_REQUEST_LOCATION) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // 用户已经授予了 ACCESS_FINE_LOCATION 权限,可以开始使用 GPS 功能 // ... } else { // 用户拒绝了授权请求,不能使用 GPS 功能 // ... } } } ``` 在上面的代码中,你需要将 MY_PERMISSIONS_REQUEST_LOCATION 替换为一个你自己定义的整数值,它用于标识该权限请求。在 onRequestPermissionsResult() 回调方法中,你可以检查用户是否授予了该权限,并相应地处理授权结果。如果用户拒绝了授权请求,你可能需要向用户解释为什么应用程序需要该权限,并提示用户在应用程序设置中手动授予权限

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值