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

xiaoxiao2021-02-28  87

一、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中

转载请注明原文地址: https://www.6miu.com/read-20992.html

最新回复(0)