Android权限

Android系统安全机制

Android是一个权限分隔的操作系统,其中每个应用程序运行在具有鲜明的标识系统(Linux的用户ID和组ID),Linux的应用程序以及系统应用程序彼此隔离。Android通过”permission”机制提供一个额外的细粒度安全特性,针对一个特定的进程可以强制执行具体的操作限制。
安全架构
安卓安全架构的中心设计思想是默认无任何应用享有任何可能对其他应用,操作系统或用户产生不利影响的操作的权限。这个包括读取或写入用户的私有数据(比如联系人或邮件),读取或写入别的应用的文件,执行网络访问,使设备保持唤醒等。
由于每个 Android 应用都是在进程沙盒中运行,因此应用必须显式共享资源和数据。它们的方法是声明需要哪些权限来获取基本沙盒未提供的额外功能。应用以静态方式声明它们需要的权限,然后 Android 系统提示用户同意。
应用沙盒不依赖用于开发应用的技术。特别是,Dalvik VM 不是安全边界,任何应用都可运行原生代码(请参阅 Android NDK)。各类应用 — Java、原生和混合 — 以同样的方式放在沙盒中,彼此采用相同程度的安全防护。
应用签署
所有 APK(.apk 文件)都必须使用证书签署,其私钥由开发者持有。此证书用于识别应用的作者。证书不需要由证书颁发机构签署;Android 应用在理想情况下可以而且通常也是使用自签名证书。证书在 Android 中的作用是识别应用的作者。这允许系统授予或拒绝应用对签名级权限的访问,以及授予或拒绝应用获得与另一应用相同的 Linux 身份的请求。

android:sharedUserId
The name of a Linux user ID that will be shared with other applications. By default, Android assigns each application its own unique user ID. However, if this attribute is set to the same value for two or more applications, they will all share the same ID — provided that they are also signed by the same certificate. Application with the same user ID can access each other’s data and, if desired, run in the same process.

用户 ID 和文件访问
在安装时,Android 为每个软件包提供唯一的 Linux 用户 ID。此 ID 在软件包在该设备上的使用寿命期间保持不变。在不同设备上,相同软件包可能有不同的 UID;重要的是每个软件包在指定设备上的 UID 是唯一的。

由于在进程级实施安全性,因此任何两个软件包的代码通常都不能在同一进程中运行,因为它们需要作为不同的 Linux 用户运行。您可以在每个软件包的 AndroidManifest.xml 的 manifest 标记中使用 sharedUserId 属性,为它们分配相同的用户 ID。这样做以后,出于安全目的,两个软件包将被视为同一个应用,具有相同的用户 ID 和文件权限。请注意,为保持安全性,只有两个签署了相同签名(并且请求相同的 sharedUserId)的应用才被分配同一用户 ID。

应用存储的任何数据都会被分配该应用的用户 ID,并且其他软件包通常无法访问这些数据。使用 getSharedPreferences(String, int)、openFileOutput(String, int) 或 openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory) 创建新文件时,可以使用 MODE_WORLD_READABLE 和/或 MODE_WORLD_WRITEABLE 标记允许任何其他软件包读取/写入文件。设置这些标记时,文件仍归您的应用所有,但其全局读取和/或写入权限已适当设置,使任何其他应用都可看见它。

使用权限
基本 Android 应用默认情况下未关联权限,这意味着它无法执行对用户体验或设备上任何数据产生不利影响的任何操作。要利用受保护的设备功能,必须在应用清单中包含一个或多个 标记。

例如,需要监控传入的短信的应用要指定:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    ...
</manifest>

如果您的应用在其清单中列出正常权限(即,不会对用户隐私或设备操作造成很大风险的权限),系统会自动授予这些权限。如果您的应用在其清单中列出危险权限(即,可能影响用户隐私或设备正常操作的权限),系统会要求用户明确授予这些权限。Android 发出请求的方式取决于系统版本,而系统版本是应用的目标:

  • 如果设备运行的是 Android 6.0(API 级别 23)或更高版本,并且应用的 targetSdkVersion 是 23 或更高版本,则应用在运行时向用户请求权限。用户可随时调用权限,因此应用在每次运行时均需检查自身是否具备所需的权限。如需了解有关在应用中请求权限的详细信息,请参阅使用系统权限培训指南。
    这里写图片描述
    这里写图片描述

  • 如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在用户安装应用时要求用户授予权限。如果将新权限添加到更新的应用版本,系统会在用户更新应用时要求授予该权限。用户一旦安装应用,他们撤销权限的唯一方式是卸载应用。
    这里写图片描述

通常,权限失效会导致 SecurityException 被扔回应用。但不能保证每个地方都是这样。例如,sendBroadcast(Intent) 方法在数据传递到每个接收者时会检查权限,在方法调用返回后,即使权限失效,您也不会收到异常。但在几乎所有情况下,权限失效会记入系统日志。

Android 系统提供的权限请参阅 Manifest.permission。此外,任何应用都可定义并实施自己的权限,因此这不是所有可能权限的详尽列表。

可能在程序运行期间的多个位置实施特定权限:

  • 在调用系统时,防止应用执行某些功能。
  • 在启动 Activity 时,防止应用启动其他应用的 Activity。
  • 在发送和接收广播时,控制谁可以接收您的广播,谁可以向您发送广播。
  • 在访问和操作内容提供程序时。
  • 绑定至服务或启动服务。

自动权限调整

随着时间的推移,平台中可能会加入新的限制,要想使用特定 API,您的应用可能必须请求之前不需要的权限。因为现有应用假设可随意获取这些 API 应用的访问权限,所以 Android 可能会将新的权限请求应用到应用清单,以免在新平台版本上中断应用。Android 将根据为 targetSdkVersion 属性提供的值决定应用是否需要权限。如果该值低于在其中添加权限的版本,则 Android 会添加该权限。

  • 例如,API 级别 4 中加入了 WRITE_EXTERNAL_STORAGE 权限,用以限制访问共享存储空间。如果您的 targetSdkVersion 为 3 或更低版本,则会向更新 Android 版本设备上的应用添加此权限。

注意:如果某权限自动添加到应用,则即使您的应用可能实际并不需要这些附加权限,Google Play 上的应用列表也会列出它们。

为避免这种情况,并且删除您不需要的默认权限,请始终将 targetSdkVersion 更新至最高版本。可在 Build.VERSION_CODES 文档中查看各版本添加的权限。

官方文档:https://developer.android.com/intl/zh-cn/guide/topics/security/permissions.html
Android的权限系统一直都是Android安全概念中一个重要的环节。

权限的分类

在Android的官方文档中,并未找到对Android权限的具体分类说明,只是在自定义权限文档中提到,按照protection level的不同Android权限有4个类别:
normal 权限是低风险的,不会对系统、用户或其他应用程序造成危害
dangerous 权限是高风险的,系统将可能要求用户输入相关信息,才会授予此权限
signature 当申请权限的应用和声明权限的应用签名一致时,系统会自动授权
signatureOrSystem 当申请权限的应用和声明权限的应用签名一致或应用是系统级别的应用时,系统会自动授权,signatureOrSystem被建议避免使用。

特殊权限
有许多权限其行为方式与正常权限及危险权限都不同。SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 特别敏感,因此大多数应用不应该使用它们。如果某应用需要其中一种权限,必须在清单中声明该权限,并且发送请求用户授权的 intent。系统将向用户显示详细管理屏幕,以响应该 intent。

如需了解有关如何请求这些权限的详情,请参阅 SYSTEM_ALERT_WINDOW 和 WRITE_SETTINGS 参考条目。
其中最重要的被广泛使用的两种是:Normal Permissions和Dangerous Permissions。

Normal Permissions
正常权限涵盖应用需要访问其沙盒外部数据或资源,但对用户隐私或其他应用操作风险很小的区域。例如,设置时区的权限就是正常权限。如果应用声明其需要正常权限,系统会自动向应用授予该权限。如需当前正常权限的完整列表,请参阅正常权限。
As of API level 23, the following permissions are classified as PROTECTION_NORMAL:

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

dangerous
危险权限涵盖应用需要涉及用户隐私信息的数据或资源,或者可能对用户存储的数据或其他应用的操作产生影响的区域。例如,能够读取用户的联系人属于危险权限。如果应用声明其需要危险权限,则用户必须明确向应用授予该权限。

所有危险的 Android 系统权限都属于权限组。如果设备运行的是 Android 6.0(API 级别 23),并且应用的 targetSdkVersion 是 23 或更高版本,则当用户请求危险权限时系统会发生以下行为:

  • 如果应用请求其清单中列出的危险权限,而应用目前在权限组中没有任何权限,则系统会向用户显示一个对话框,描述应用要访问的权限组。对话框不描述该组内的具体权限。例如,如果应用请求 READ_CONTACTS 权限,系统对话框只说明该应用需要访问设备的联系信息。如果用户批准,系统将向应用授予其请求的权限。
  • 如果应用请求其清单中列出的危险权限,而应用在同一权限组中已有另一项危险权限,则系统会立即授予该权限,而无需与用户进行任何交互。例如,如果某应用已经请求并且被授予了 READ_CONTACTS 权限,然后它又请求 WRITE_CONTACTS,系统将立即授予该权限。

任何权限都可属于一个权限组,包括正常权限和应用定义的权限。但权限组仅当权限危险时才影响用户体验。可以忽略正常权限的权限组。

如果设备运行的是 Android 5.1(API 级别 22)或更低版本,并且应用的 targetSdkVersion 是 22 或更低版本,则系统会在安装时要求用户授予权限。再次强调,系统只告诉用户应用需要的权限组,而不告知具体权限。

权限组权限
CALENDARREAD_CALENDAR
WRITE_CALENDAR
CAMERACAMERA
CONTACTSREAD_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
LOCATIONACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
MICROPHONERECORD_AUDIO
PHONEREAD_PHONE_STATE
CALL_PHONE
READ_CALL_LOG
WRITE_CALL_LOG
ADD_VOICEMAIL
USE_SIP
PROCESS_OUTGOING_CALLS
SENSORSBODY_SENSORS
SMSSEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
STORAGEREAD_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

定义和实施权限

要实施您自己的权限,必须先使用一个或多个 元素在 AndroidManifest.xml 中声明它们。

例如,想要控制谁可以开始其中一个 Activity 的应用可如下所示声明此操作的权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp" >
    <permission android:name="com.example.myapp.permission.DEADLY_ACTIVITY"
        android:label="@string/permlab_deadlyActivity"
        android:description="@string/permdesc_deadlyActivity"
        android:permissionGroup="android.permission-group.COST_MONEY"
        android:protectionLevel="dangerous" />
    ...
</manifest>

注:系统不允许多个软件包使用同一名称声明权限,除非所有软件包都使用同一证书签署。如果软件包声明权限,则系统不允许用户安装具有相同权限名称的其他软件包,除非这些软件包使用与第一个软件包相同的证书签署。为避免命名冲突,建议对自定义权限使用相反域名样式命名,例如 com.example.myapp.ENGAGE_HYPERSPACE。

protectionLevel 属性是必要属性,用于指示系统如何向用户告知需要权限的应用,或者谁可以拥有该权限,具体如链接的文档中所述。

android:permissionGroup 属性是可选属性,只是用于帮助系统向用户显示权限。大多数情况下,您要将此设为标准系统组(列在 android.Manifest.permission_group 中),但您也可以自己定义一个组。建议使用现有的组,因为这样可简化向用户显示的权限 UI。

自定义权限建议

应用可以定义自己的自定义权限,并通过定义 元素请求其他应用的自定义权限。不过,您应该仔细评估您的应用是否有必要这样做。

  • 如果要设计一套向彼此显示功能的应用,请尽可能将应用设计为每个权限只定义一次。如果所有应用并非使用同一证书签署,则必须这样做。即使所有应用使用同一证书签署,最佳做法也是每个权限只定义一次。
    如果功能仅适用于使用与提供应用相同的签名所签署的应用,您可能可以使用签名检查避免定义自定义权限。当一个应用向另一个应用发出请求时,第二个应用可在遵从该请求之前验证这两个应用是否使用同一证书签署。
  • 如果您要开发一套只在您自己的设备上运行的应用,则应开发并安装管理该套件中所有应用权限的软件包。此软件包本身无需提供任何服务。它只是声明所有权限,然后套件中的其他应用通过 元素请求这些权限。

应用申请Dangerous Permissions

对于6.0以前的Android系统,声明在manifest中的权限在安装时会全部授权,如果用户不授权,则app根本无法安装。对于6.0及以后的系统,每一个用到的dangerous permission除要在Manifest.xml中声明外,还需要在app运行过程中向用户申请,用户可以授予或拒绝每一个权限。
这里写图片描述

这里写图片描述

原本一段顺序执行的代码,将会由于用户的选择,面临三种不同的情况:
1. 用户点击“允许”,app获得通讯录组权限(此处申请的是READ_CONTACTS);
2. 用户点击“拒绝”,app无法获得通讯录组权限,读取通讯录功能无法正常使用,下次使用该功能仍然会弹出申请权限的通知;
3. 用户点击“拒绝”并同时勾选“不再询问”,app无法获得通讯录组权限,此后再使用该功能,将不会弹出申请权限的通知,要想授权该权限,用户只能去“设置”界面手动开启“通讯录”权限。

这里写图片描述

6.0新增的权限相关API

检查权限

弊端:判断权限时只能一个个权限进行判断,如果我们同时需要多个权限,只能多次调用checkSelfPermission方法。

这里写图片描述

申请权限
申请权限时,Android会弹出一个对话框,由用户选择是否授权app所申请的权限。这个流程是异步的,只有在用户进行了选择之后,系统才会调用回调方法通知app用户已经进行了选择,可以进行接下来的操作了。
调用requestPermissions()方法弹出的对话框,是系统默认的标准对话框,不能进行任何的修改(样式、提示文字)。
在进行权限申请时,传入的是String数组,因此可以同时申请多个权限。

传入的requestCode用来在回调方法中区别申请的权限。

这里写图片描述

解释为什么要申请权限

在很多时候,用户并不清楚我们为什么要用到某个权限,比如在SDK初始化时,需要读取手机信息,这时候用到了READ_PHONE_STATE权限,但是用户可能并不清楚为什么要读取手机状态,很可能会禁止这个权限,但如果获取不到手机信息,SDK根本无法正常工作。这时候我们就需要解释给用户,为什么我们要申请这个权限,没有这个权限可能会导致什么问题。
申请权限时的对话框我们不能自定义,但Android提供了一个方法给开发者,用来判断何时需要向用户解释申请某个权限的原因。
这里写图片描述

这个方法只能用来判断是否需要向用户解释申请权限的原因了,通过何种方式,进行怎样的解释,需要开发者自己定义。
如果app从未申请过权限,此时调用shouldShowRequestPermissionRationale()的返回值是成false;
如果app请求过权限,但是用户拒绝了,此时调用shouldShowRequestPermissionRationale(),返回值为true
如果用户关闭权限并且勾选了“不再询问”,那么shouldShowRequestPermissionRationale()的返回值会变成false。

处理回调
当用户对权限申请弹出的对话框进行操作时,系统会调用方法onRequestPermissionsResult(),开发者需要重写这个方法,来确认权限是否被用户授予。
这里写图片描述

在方法中,requestCode就是在申请权限requestPermissions()中传入的值。

弊端:在回调中需要分别对权限名称和授权结果进行判断,然后根据结果进行不同处理。

前面提到,Dangerous Permissions是按照组划分的,在申请权限时,系统弹出的对话框是按照组的名称进行提示的,不会指定每一个单独的权限。当组中的任何一个权限被授权时,其它权限默认都被授权,不会再弹出对话框。
这里写图片描述

我们在实际开发过程中,不能依赖这个组权限,要对每一个用到的权限都进行判断,然后根据判断结果进行处理。因为在后面的版本中,这个组的划分可能会进行修改,到时候可能会影响的app的正常使用。

Signature 之 Special Permissions

我们在开发时经常使用的权限是Normal PermissionsDangerous Permissions,但是还有一对常用的权限,它们的protection levelSignature,常被称为特殊权限,它们的表现既不像normal permissions,也不像dangerous permissions

这两个权限是SYSTEM_ALERT_WINDOWWRITE_SETTINGS

SYSTEM_ALERT_WINDOW

这里写图片描述

注意:当窗口类型为“TYPE_PHONE”时,也需要使用到这个权限。
在6.0之前,Android原生系统并没有对这个浮标的使用做限制(一些定制的Android系统做了限制),但是从6.0开始,浮标权限默认为未授权,在使用浮标之前,就要对浮标的权限进行判断和申请。

允许应用创建类型为TYPE_SYSTEM_ALERT的窗口,以显示在其他所有应用的顶层,几乎很少有应用会用到这个权限。创建的窗口主要用来与用户进行系统级别的交互。

SYSTEM_ALERT_WINDOW权限的级别是:signature。

WRITE_SETTINGS
是允许程序读取或写入系统设置,同样是signature级别的。
这里写图片描述

下面以SYSTEM_ALERT_WINDOW为例讲解Special Permissions权限的判断和获取。

检查权限
在判断SYSTEM_ALERT_WINDOW时,使用系统提供的专门的判断方法Settings.canDrawOverlays()
需要注意的是,这个方法是在6.0才出现的,因此在开发过程中,为了兼容不同的系统版本,在调用之前需要先判断当前系统的版本,否则会在编译过程中报错。

由于我们的app不可能和声明这两个special权限的app有相同的签名,因此我们在使用时必须向系统申请权限。
Dangerous Permissions申请权限不同,当申请Special Permissions的权限时,不是通过Android提供的对话框进行处理,而是使用Intent到应用设置界面进行修改,发送的actionACTION_MANAGE_OVERLAY_PERMISSION
这里写图片描述

处理回调
当点击返回按键的时候,因为打开时使用的是方法startActivityForResult(),因此可以在activity中通过onActivityResult查看权限是否授予。

官方对权限使用的建议

对于一个app来说,权限申请很容易惹恼用户。如果一个用户发现一个app用起来很烦人,或者用户担心app会对自己的信息做什么,他们也许会避免使用app或者直接整个删除app。
为了避免上述的情况,Google为开发者提出了一些使用权限时的建议。

一、Consider Using an Intent

在很多情况下,我们可以有两种方式来执行一个任务。让自己的app来执行这个操作,或者,让app使用一个Intent去打开另一个app,让另外的app执行这个任务。
比如当我们的app需要使用相机功能拍照时,我们可以自己申请CAMERA权限然后使用相机API进行拍照,或者我们可以使用ACTION_IMAGE_CAPTURE来选择一个相机app进行拍照,然后通过onActivityResult回调来获取照片。

使用权限时:
我们可以完全控制用户的行为,但是这会导致任务复杂化,因为我们要设计合适的UI并在运行时(或安装时)获取对应的权限,如果没有权限会导致任务完全无法执行。

使用intent:
无法控制用户的行为,任务变得简单,不用设计UI。

选择哪种方法,是根据产品需求等因素来定,两种情况都有各自的优缺点。

二、Only Ask for Permissions You Need

无论是在6.0之后还是之前,每一个申请的权限都会强制用户做一个选择,开发者应该尽量减少这种情况发生的次数。

如果用户用的是低于6.0的版本,用户安装时需要许可这些权限,如果用户发现这个许可的list很长或者看起来不合适,用户可能觉得不会安装app。
如果用户使用的Android版本是6.0(API23)或者更新的版本,每次用户尝试一下新的需要权限的app功能,app需要打断用户的操作来进行权限许可请求。

三、Don’t Overwhelm the User

如果用户的版本是Android 6.0或者之后的,用户需要在app运行时授予权限。如果让用户一下子面对许多的请求,这可能惹怒用户,导致他们卸载app。取而代之,开发者应该在应用需要权限的时候再请求权限。
比如在一个app中有拍照和分享照片的功能,当执行到拍照时就申请 CAMERA 权限,只有当用户进行“分享”操作时,才申请 READ_CONTACTS 权限。

四、Explain Why You Need Permissions

当你调用requestPermissions方法来表名你的app需要的权限时,系统会显示权限对话框 ,但是并不会说为什么需要这些权限。在一些情况下可能会导致用户莫名其妙,不了解为什么要使用到这个权限。因此在申请权限之前最好向用户解释为什么需要这个权限。

比如,一个摄影app也许会需要定位服务,以便标记照片的位置。一个普通的用户或许不会明白为什么拍个照片需要包含位置信息,并且会困惑为什么摄影app需要知道地址信息。

五、Test for Both Permissions Models

在Android6.0之前,如果app在运行,那么app就拥有它在Manifest文件所列举的所有权限。但是从6.0开始,用户可以随时收回权限,因此开发者需要在不同的情况下充分测试app。
比如:
测试权限被拒绝时是否会影响app的运行;
如果一个功能使用到多个权限,那么需要测试授予和拒绝这些权限时的不同组合,确保app可以完美地处理所有情况。

EasyPermissions开源库

在进行权限适配时,按照Google官方建议的处理方法和流程去操作,代码量和需要处理的地方较多,相对繁琐:
在判断权限时,每次只能检查一个权限;
申请权限之前,需要根据shouldShowRequestPermissionRationale的值来提示用户申请权限的原因;
回调处理麻烦,需要根据结果参数对每一个权限进行单独的判断

因此Google推出了一个开源库Easypermissions。

使用方法:

  • 一、添加依赖到 Gradle :
dependencies {
  compile 'pub.devrel:easypermissions:0.1.5'
}
  • 二、实现*PermissionCallbacks*接口
    在使用 EasyPermissions 时,需要首先在使用到危险权限的Activity 或者 Fragment 中实现 EasyPermissions.PermissionCallbacks 接口,并且重写方法onRequestPermissionsResult

  • 三、权限判断
    使用 EasyPermissions.hasPermissions(…) 去判断 app 是否已经有权限了。该方法的最后一个参数是可变数组形式的,所以可以一次性查询多个权限,系统API无法做到这点。

  • 四、权限请求
    使用 EasyPermissions.requestPermissions 去请求权限。该方法在请求权限的同时有必要的话会显示使用权限理由。 requestCode 对于该方法来说必须是唯一的,同时最后一个参数也是可变数组形式的,所以可以一次性请求多个权限。

  • 五、回调处理
    使用 AfterPermissioonGranted 注解。这是可选的,但是提供出来是为了方便。如果所有的请求的权限都被授予了,被注解的方法将会被执行,这样做是为了简化通常的请求权限成功之后再调用方法的流程。
    同时也可以在 onPermissionsGranted 的回调中添加逻辑操作。

从Android 6.0开始,权限机制进行了很大的变化,用户可以随时对已经授予的权限进行撤掉操作。
如果应用的targetSDKVersion指定为22及以下,那么在用户撤销的时候,系统会给用户警告,告诉用户这是一个很危险的行为,撤销权限会影响到应用的正常使用。
但是,当targetSDKVersion为23及以上的时候,系统会认为开发者已经对Android 6.0进行了充分的兼容和测试,当撤销权限的时候,就不会再进行任何的提示。因此,就需要我们在使用到任何危险之前,都先要进行判断。
如果应用进程不存在,也就是应用关闭的时候用户去撤销权限,只要在使用时做好了判断和申请机制就可以,但是如果应用正在后台运行,这时候用户去撤销权限,就出现了问题。

应用在后台运行时,从设置中撤销已经授权的权限,会导致应用重建生成新的进程,这时候再从后台恢复应用,activity的生命周期不是从onResume开始执行,而是从onCreate开始。
而且通过测试发现,权限从 有->无、有->无->有、无->有->无都会导致原有进程被杀死,产生新的进程。

当进程重建时,系统会自动重建之前退出的activity。
比如之前打开的顺序是A->B->C,那么首先会重建C,然后通过back键返回到B,在重建B,依次类推。

还有一种情况,就是如果我们需要同时申请同一个权限多次,那么系统只弹出一个判断的dialog,并且只返回第一个申请的requestCode。

SdkVersion

在一个build.gradle文件中,我们可以看到三个SdkVersion,分别是:compileSdkVersionminSdkVersiontargetSdkVersion
一句话介绍这三个名词:分别控制可以使用哪些 API ,要求的 API 级别是什么,以及应用的兼容模式。

compileSdkVersion

compileSdkVersion 告诉 Gradle 用哪个 Android SDK 版本编译你的应用。使用任何新添加的 API 就需要使用对应 LevelAndroid SDK
需要强调的是修改 compileSdkVersion 不会改变运行时的行为。当修改了 compileSdkVersion 的时候,可能会出现新的编译警告、编译错误,但新的 compileSdkVersion 不会被包含到 APK 中,它纯粹只是在编译的时候使用。
注意:如果使用 Support Library ,那么使用最新发布的 Support Library 就需要使用最新的 SDK 编译。例如,要使用 23.1.1 版本的 Support LibrarycompileSdkVersion 就必需至少是 23 (大版本号要一致!)。这是因为新版的 Support Library 随着新的系统版本而发布,它为系统新增加的 API 和新特性提供兼容性支持。

minSdkVersion

minSdkVersion 则是应用可以运行的最低要求。minSdkVersionGoogle Play 商店用来判断用户设备是否可以安装某个应用的标志之一。
lint可以通过检查minSdkVersion来提醒我们是否使用了高于minSdkVersionAPI,避免调用不存在的API时影响运行。必须使用较高版本的API,一般都是通过运行时检查系统版本的方式解决。
注意:在项目中使用的库可能设置了他们自己的minSdkVersion ,那么应用设置的minSdkVersion 必需大于等于这些库的 minSdkVersion

targetSdkVersion

targetSdkVersionAndroid 提供向前兼容的主要依据,在应用的 targetSdkVersion 没有更新之前系统不会应用最新的行为变化。这个属性通知系统,你已经针对这个指定的目标版本测试过你的程序。
随着 Android 系统的升级,某个系统的 API 或者模块的行为可能会发生改变,但是为了保证老 APK 的行为还是和以前兼容。只要 APKtargetSdkVersion 不变,即使这个 APK 安装在新 Android 系统上,其行为还是保持老的系统上的行为,这样就保证了系统对老应用的前向兼容性。
注意:在Android Studio上创建新的项目时,会默认使用最新的SDK版本作为targetSdkVersion ,当我们的app由于各种原因暂时无法适应最新的SDK版本时,需要降低build.gradle中的targetSdkVersion版本号。

Example 1

targetSdkVersion 设置为23的时候,安装在Android M系统上的应用会遵循最新的权限要求,使用的危险权限需要动态获取,这时候就要求开发者已经在应用中对权限进行了判断和获取。如果由于开发时间问题,无法保证适配Android M的权限要求,这时候就可以降低targetSdkVersion的版本。

Example 2

Android 4.4 (API 19)以后,AlarmManagerset()setRepeat() 这两个 API 的行为发生了变化。在 Android 4.4 以前,这两个 API 设置的都是精确的时间,系统能保证在 API 设置的时间点上唤醒 Alarm。因为省电原因 Android 4.4 系统实现了 AlarmManager 的对齐唤醒,这两个 API 设置唤醒的时间,系统都对待成不精确的时间,系统只能保证在你设置的时间点之后某个时间唤醒。
这时,虽然 API 没有任何变化,但是实际上 API 的行为却发生了变化,如果老的 APK 中使用了此 API,并且在应用中的行为非常依赖 AlarmManager 在精确的时间唤醒,例如闹钟应用。如果 Android 系统不能保证兼容,老的 APK 安装在新的系统上,就会出现问题。
Android 系统是怎么保证这种兼容性的呢?这时候 targetSdkVersion 就起作用了。APK 在调用系统 AlarmManagerset() 或者 setRepeat() 的时候,系统首先会查一下调用的 APKtargetSdkVersion 信息,如果小于 19,就还是按照老的行为,即精确设置唤醒时间,否者执行新的行为。

Android系统中,一些行为的变化对用户来说是非常明显的(例如Android M的运行时权限),所以将targetSdkVersion 设置为最新的SDK是很重要的事情,但是在更新targetSdkVersion 之前,一定要进行充分的测试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值