Android版本适配
Android6 (23)
Target 23
运行时权限
Android6.0将权限分为安装权限和运行时权限,安装权限仍然只需要声明即可,运行时权限需要调用代码请求运行时权限,当用户同意时才获得
相关方法
方法 | 作用 | 返回值 |
---|---|---|
ContextCompat.checkSelfPermission(Context context, String permission) | 检测permission权限是否允许 | PERMISSION_DENIED 拒绝 PERMISSION_GRANTED同意 |
ActivityCompat.requestPermissions(Activity activity,String[] permissions, int requestCode) | 向用户申请一组权限,在onRequestPermissionsResult回调中判断请求结的 | 无 |
Activity.onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) | requestPermissions方法调用后回调到此方法 | 无 |
Activity.shouldShowRequestPermissionRationale(String permission) | 是否需要向用户展示权限说明 对应权限从未requestPermission申请过->false 第一次requestPermission申请权限,用户点击“拒绝”,没有选择“不再询问”->true 第n>1次请求权限,拒绝->false 权限已同意->false | 无 |
请求流程
- checkSelfPermission检查是否有权限,无权限->2,有权限->4
- shouldShowRequestPermissionRationale是否展示权限说明,是则弹出说明->3,否则表示用户点了不再提示,如果app不是一定需要此权限则结束业务流程(比如相机之类),如果必须要才能正常运行则弹窗提示,并引导用户去权限管理中授予权限
- requestPermissions请求权限,同意->4,不同意则根据实际需求而定
- 执行业务
All
移除HttpClient
不再支持HttpClient类,如果项目需要可以在build.gradle中申明
android {
useLibrary ‘org.apache.http.legacy’
}
Android7.0 (24)
Target 24
网络连接广播CONNECTIVITY_ACTION
静态注册的广播不会接收到CONNECTIVITY_ACTION,动态注册仍然可以
应用间共享文件FileProvider
禁止在应用外部公开 file:// 的URI。如果一项包含文件 URI 的 intent 离开您的应用,则应用出现 FileUriExposedException 异常,应使用FileProvider类公开content://的URI
具体看这个Android 7.0 行为变更 通过FileProvider在应用间共享文件
All
取消ACTION_NEW_PICTURE,ACTION_NEW_VIDEO广播
无法发送或接收 ACTION_NEW_PICTURE 或 ACTION_NEW_VIDEO 广播
Apk签名V2
Android 7.0 引入一项新的应用签名方案 APK Signature Scheme v2,AS打包时可以选择v1,v2签名选项
1.只勾选V1签名就是传统方案签署,但是在 Android 7.0 上不会使用V2安全的验证方式。
2.只勾选V2签名7.0以下系统无法安装app,7.0 上则会使用了V2安全的验证方式。
3.同时勾选V1和V2则所有版本都没问题。
Android 7.1 (25)
新增应用快捷方式
长按桌面图标,显示快捷方式
详见Android Developer | 创建快捷方式|Android开发
Android 8.0 (26)
Target 26
窗口类型变更
如果应用使用 SYSTEM_ALERT_WINDOW 权限并且尝试使用以下窗口类型之一来在其他应用和系统窗口上方显示提醒窗口
TYPE_PHONE
TYPE_PRIORITY_PHONE
TYPE_SYSTEM_ALERT
TYPE_SYSTEM_OVERLAY
TYPE_SYSTEM_ERROR
那么,这些窗口将始终显示在使用 TYPE_APPLICATION_OVERLAY 窗口类型的窗口下方。如果应用针对的是 Android 8.0,则应用会使用 TYPE_APPLICATION_OVERLAY 窗口类型来显示提醒窗口
内容变更通知
Android 8.0 更改了 ContentResolver.notifyChange() 和 registerContentObserver(Uri, boolean, ContentObserver) 在针对 Android 8.0 的应用中的行为方式。
现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider。使用相关权限定义一个有效的 ContentProvider 可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用
通知渠道
新增通知渠道用于管理不同类型的通知,需要指定渠道否则不弹出通知
详见Android 8.0 创建管理通知渠道Notification
安装apk权限
安装apk需要线获取安装未知来源应用的权限
详见Android8.0 允许安装未知来源权限
广播限制
- 静态注册器无法接收隐式广播
- 静态注册器可以接收显式广播
- 动态注册器(Context.registerReceiver() )可以接受隐式和显式广播
Android Developer | 广播限制
android 8.0+后台广播限制
All
后台服务限制
当应用处于后台时,不允许调用startService创建后台服务,应使用startForgroundService启动前台服务,并在5s内调用startForeground函数创建一个前台通知
透明Activity异常
Caused by: java.lang.IllegalStateException: Only fullscreen opaque activities can request orientation
这是一个bug,当Activity全屏透明,且android:screenOrientation=“portrait|landscape”指定屏幕方向时,会出现此异常
Android 8.1 (27)
Android 9.0 (28)
Target 28
构建序列号弃用
在 Android 9 中,Build.SERIAL 始终设为 “UNKNOWN”,以保护用户隐私。
如果您的应用需要访问设备的硬件序列号,您应改为请求 READ_PHONE_STATE 权限,然后调用 getSerial()。
前台服务权限
使用startForgroundService需要有前台服务权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
对使用非 SDK 接口的限制
为帮助确保应用稳定性和兼容性,此平台对某些非 SDK 函数和字段的使用进行了限制;无论您是直接访问这些函数和字段,还是通过反射或 JNI 访问,这些限制均适用。 在 Android 9 中,您的应用可以继续访问这些受限的接口;该平台通过 toast 和日志条目提醒您注意这些接口。 如果您的应用显示这样的 toast,则必须寻求受限接口之外的其他实现策略。
Android Developer | 针对非 SDK 接口的限制
禁止明文http传输
默认限制只能使用https传输,非https请求会被禁止
解决方案
<!--AndroidManifest-->
<application
android:networkSecurityConfig="@xml/network_security_config">
</application>
<!--network_security_config-->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
All
强制执行 FLAG_ACTIVITY_NEW_TASK 要求
在 Android 9 中,不能从非 Activity 环境中启动 Activity,除非添加 FLAG_ACTIVITY_NEW_TASK的flag。 如果尝试在不传递此标志的情况下启动 Activity,则该 Activity 不会启动
限制访问通话记录
Android 9 引入 CALL_LOG 权限组并将 READ_CALL_LOG、WRITE_CALL_LOG 和 PROCESS_OUTGOING_CALLS 权限移入该组。在之前的版本中,这些权限位于 PHONE权限组。
在未首先获得 READ_CALL_LOG 权限的情况下,除了应用的用例需要的其他权限之外,运行于 Android 9 上的应用无法读取电话号码或手机状态。
后台对传感器的访问受限
应用处于后台时
- 您的应用不能访问麦克风或摄像头。
- 使用连续报告模式的传感器(例如加速度计和陀螺仪)不会接收事件。
- 使用变化或一次性报告模式的传感器不会接收事件。
如果您的应用需要在运行 Android 9 的设备上检测传感器事件,请使用前台服务。
Android 10.0 (29)
Target 29
分区存储
默认情况下,对于以 Android 10 及更高版本为目标平台的应用,其访问权限范围限定为外部存储,即分区存储。此类应用可以查看外部存储设备内以下类型的文件,无需请求任何与存储相关的用户权限:
- 特定于应用的目录中的文件(使用 getExternalFilesDir() 访问)。
- 应用创建的照片、视频和音频片段(通过媒体库访问)。
针对全屏 Intent 的权限变更
如果应用以 Android 10 或更高版本为目标平台并使用涉及全屏 intent 的通知,则必须在应用的清单文件中请求 USE_FULL_SCREEN_INTENT 权限,这是普通权限不需要动态申请
All
在后台运行时访问设备位置信息需要权限
为了让用户更好地控制应用对位置信息的访问权限,Android 10 引入了 ACCESS_BACKGROUND_LOCATION 权限。
与 ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION 权限不同,ACCESS_BACKGROUND_LOCATION 权限仅会影响应用在后台运行时对位置信息的访问权限。
以下条件视为不在后台运行
- 属于该应用的 Activity 可见。
- 该应用运行的某个前台设备已声明前台服务类型为 location。
要声明您的应用中的某个服务的前台服务类型,请将应用的 targetSdkVersion 或 compileSdkVersion 设置为 29 或更高版本。详细了解前台服务如何继续执行用户发起的需要访问位置信息的操作。
如果您的应用在 >=Android 10.0 上运行,但其targetversion是 <=Android 9.0,则该平台具有以下行为:
- 如果您的应用为 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION 声明了 元素,则系统会在安装期间自动为 ACCESS_BACKGROUND_LOCATION 添加 元素。
- 如果您的应用请求了 ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION,系统会自动将 ACCESS_BACKGROUND_LOCATION 添加到请求中。
后台启动 Activity受限
后台状态下不允许启动Activity
在 Android 10 或更高版本上运行的应用只有在满足以下一项或多项条件时,才能启动 Activity:
-
应用具有可见窗口,例如前台 Activity。
-
应用在前台任务的返回栈中拥有 Activity。
-
应用在 Recents 屏幕上现有任务的返回栈中拥有 Activity。
注意:当此类应用尝试启动新的 Activity 时,系统会将该 Activity 放置到应用现有任务的顶部,但不会离开当前可见的任务。当用户稍后返回应用任务时,系统会启动新的 Activity,而不是之前放置在应用任务顶部的 Activity。 -
应用的某个 Activity 刚在不久前启动。
-
应用最近为某个 Activity 调用了 finish()。这仅适用于在调用 finish() 时,应用在前台或前台任务的返回栈中拥有 Activity 的情况。
-
应用具有受系统约束的服务。此情况仅适用于以下服务,这些服务可能需要启动界面:AccessibilityService、AutofillService、CallRedirectionService、HostApduService、InCallService、TileService、VoiceInteractionService 和 VrListenerService。
-
应用中的某个服务受另一个可见应用约束。请注意,绑定到服务的应用必须保持可见,以便后台应用成功启动 Activity。
-
应用收到系统的 PendingIntent 通知。对于服务和广播接收器的挂起 Intent,应用可在该挂起 Intent 发送几秒钟后启动 Activity。
-
应用收到另一个可见应用发送的 PendingIntent。
-
应用收到它应该在其中启动界面的系统广播。示例包括 ACTION_NEW_OUTGOING_CALL 和 SECRET_CODE_ACTION。应用可在广播发送几秒钟后启动 Activity。
-
应用通过 CompanionDeviceManager API 与配套硬件设备相关联。此 API 支持应用启动 API,以响应用户在配对设备上执行的操作。
-
应用是在设备所有者模式下运行的设备政策控制器。示例用例包括完全托管的企业设备,以及数字标识牌和自助服务终端等专用设备。
-
用户已向应用授予 SYSTEM_ALERT_WINDOW 权限。
SYSTEM_ALERT_WINDOW权限变更
在 Android 10设备上运行的应用无法获得 SYSTEM_ALERT_WINDOW 权限。这是因为绘制叠加层窗口会使用过多的内存,这对低内存 Android 设备的性能十分有害。
如果在搭载 Android 9 或更低版本设备上运行的应用获得了 SYSTEM_ALERT_WINDOW 权限,则即使设备升级到 Android 10,也会保留此权限。不过,尚不具有此权限的应用在设备升级后便无法获得此权限了。
如果 Android 10设备上的应用发送具有 ACTION_MANAGE_OVERLAY_PERMISSION 操作的 intent,则系统会自动拒绝此请求,并将用户转到设置屏幕,上面会显示不允许授予此权限,原因是它会减慢设备的运行速度。如果Android 10设备上的应用调用 Settings.canDrawOverlays(),则此方法始终返回 false。这些限制不适用于在设备升级到 Android 10 之前便已获取到 SYSTEM_ALERT_WINDOW 权限的应用。
限制访问不可重置的设备标识符
从 Android 10 开始,应用必须具有 READ_PRIVILEGED_PHONE_STATE 特许权限才能访问设备的不可重置标识符(包含 IMEI 和序列号)。
受影响的方法包括:
- Build
- getSerial()
- TelephonyManager
- getImei()
- getDeviceId()
- getMeid()
- getSimSerialNumber()
- getSubscriberId()
如果您的应用没有该权限,但您仍尝试查询不可重置标识符的相关信息,则平台的响应会因目标 SDK 版本而异:
- 如果应用以 Android 10 或更高版本为目标平台,则会发生 SecurityException。
- 如果应用以 Android 9(API 级别 28)或更低版本为目标平台,则相应方法会返回 null 或占位符数据(如果应用具有 READ_PHONE_STATE 权限)。否则,会发生 SecurityException。
限制了对剪贴板数据的访问权限
除非应用是默认输入法 (IME) 或是目前处于焦点的应用,否则它无法访问 Android 10 或更高版本平台上的剪贴板数据
Android 11.0 (30)
Target 30
权限变化
单次授权
从 Android 11 开始,每当应用请求与位置信息、麦克风或摄像头相关的权限时,面向用户的权限对话框会包含仅限这一次选项。如果用户在对话框中选择此选项,系统会向应用授予临时的单次授权。
自动重置未使用的应用的权限
如果应用以 Android 11 或更高版本为目标平台并且数月未使用,系统会通过自动重置用户已授予应用的运行时敏感权限来保护用户数据。此操作与用户在系统设置中查看权限并将应用的访问权限级别更改为拒绝的做法效果一样
权限对话框的可见性
从 Android 11 开始,在应用安装到设备上后,如果用户在使用过程中多次针对某项特定的权限点按拒绝,那么在您的应用再次请求该权限时,用户将不会看到系统权限对话框。该操作表示用户希望“不再询问”。
获取电话号码权限变更
如果您的应用以 Android 11 或更高版本为目标平台,并且需要访问以下列表中显示的电话号码 API,则必须请求 READ_PHONE_NUMBERS 权限,而不是 READ_PHONE_STATE 权限。
- TelephonyManager 类和 TelecomManager 类中的 getLine1Number() 方法。
- TelephonyManager 类中不受支持的 getMsisdn() 方法。
如果您的应用声明 READ_PHONE_STATE 以调用前面列表中的方法以外的方法,您可以继续在所有 Android 版本中请求 READ_PHONE_STATE。不过,如果您仅对前面列表中的方法使用 READ_PHONE_STATE 权限,请按以下方式更新您的清单文件:
- 更改 READ_PHONE_STATE 的声明,以使您的应用仅在 Android 10(API 级别 29)及更低版本中使用该权限。
- 添加 READ_PHONE_NUMBERS 权限。
<manifest>
<!-- Grants the READ_PHONE_STATE permission only on devices that run
Android 10 (API level 29) and lower. -->
<uses-permission android:name="READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="READ_PHONE_NUMBERS" />
</manifest>
获取应用包信息权限
Android 11 更改了应用查询用户已在设备上安装的其他应用以及与之交互的方式。使用 元素,应用可以定义一组自身可访问的其他软件包。通过告知系统应向您的应用显示哪些其他软件包,此元素有助于鼓励最小权限原则。此外,此元素还可帮助 Google Play 等应用商店评估应用为用户提供的隐私权和安全性。
详见Android11及以上APP无法获取到所有安装包的问题
All
前台服务限制访问位置、相机和麦克风
当您的应用在后台运行时启动前台服务时,前台服务具有以下限制:
- 除非用户已授予 ACCESS_BACKGROUND_LOCATION 您应用程序的权限,否则前台服务无法访问位置。
- 前台服务无法访问麦克风或摄像头。
在某些情况下,即使应用程序 在后台运行时启动了前台服务,它仍然可以在应用程序在前台运行时(“使用中”)访问位置、摄像头和麦克风信息。
在这些相同的情况下,如果服务声明 前台服务类型并由location具有权限的应用程序启动 ACCESS_BACKGROUND_LOCATION ,则该服务可以始终访问位置信息,即使应用程序在后台运行时也是如此。
- 系统组件启动服务。
- 该服务首先与应用程序小部件进行交互。
- 该服务从与通知交互开始。
- PendingIntent该服务以从不同的可见应用程序发送的形式开始 。
- 该服务由一个应用程序启动,该应用程序是在设备所有者模式下运行的设备策略控制器。
- 该服务由提供VoiceInteractionService.
- 该服务由具有特权权限的应用程序启动 START_ACTIVITIES_FROM_BACKGROUND。
强制分区存储
Android 12.0 (31)
Target 31
自定义通知视图变化
包含自定义内容视图的通知将不再使用完整通知区域,系统会应用标准模板确保自定义通知在所有状态下都与其他通知相同
显示声明exported
使用 intent 过滤器的组件,必须为这些应用组件显式声明 android:exported 属性
后台启动限制
应用在后台运行时无法启动前台服务,少数特殊情况除外。如果应用程序在后台运行时尝试启动前台服务,而前台服务不满足其中一种异常情况,系统会抛出一个ForegroundServiceStartNotAllowedException.
注意:一个应用A调用Context.startForegroundService()启动另一个应用B的前台服务,当A和B的targetversion都>=32时这个限制才生效
在以下情况下,即使您的应用程序在后台运行,您的应用程序也可以启动前台服务:
- 您的应用从用户可见状态(例如 活动)转换。
- 您的应用程序可以从后台启动活动,但应用程序在现有任务的后台堆栈中有活动的情况除外。
- 您的应用程序使用Firebase Cloud Messaging接收高优先级消息。
注意:如果应用程序未使用高优先级消息向用户显示时间敏感内容,系统可以将高优先级消息降级为正常优先级。如果消息的优先级被降级,您的应用程序将无法启动前台服务,并且尝试启动一个会导致ForegroundServiceStartNotAllowedException.
因此,建议 在尝试启动前台服务之前检查RemoteMessage.getPriority()
并确认它的结果。PRIORITY_HIGH有关高优先级消息以及何时使用它们的指南,请参阅FCM 的文档。
-
用户对与您的应用程序相关的 UI 元素执行操作。例如,它们可能与气泡、 通知、 小部件或活动进行交互。
-
您的应用调用确切的警报来完成用户请求的操作。
-
您的应用程序是设备当前的输入法。
-
您的应用收到与地理围栏或活动识别转换相关的事件 。
-
在设备重新启动并 在广播接收器ACTION_BOOT_COMPLETED中接收到, ACTION_LOCKED_BOOT_COMPLETED或ACTION_MY_PACKAGE_REPLACED intent 操作后。
-
您的应用程序在广播接收器中接收 ACTION_TIMEZONE_CHANGED、 ACTION_TIME_CHANGED或 intent 操作。ACTION_LOCALE_CHANGED
-
BLUETOOTH_CONNECT 您的应用程序收到需要或权限的蓝牙广播 BLUETOOTH_SCAN 。应用必须使用Companion Device Manager并声明 REQUEST_COMPANION_RUN_IN_BACKGROUND或REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND权限。
-
具有特定系统角色或权限的应用程序,例如设备所有者 和配置文件所有者。
-
您的应用程序使用配套设备管理器并声明 REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND 权限或REQUEST_COMPANION_RUN_IN_BACKGROUND 权限。尽可能使用 REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND.
-
用户关闭了您应用的电池优化。您可以通过将用户发送到系统设置中您应用的应用信息页面来帮助用户找到此选项。为此,调用包含 ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS 意图操作的意图
All
应用启动画面SplashScreen
从 Android 12 开始,在所有应用的冷启动和温启动期间,系统始终会应用新的 Android 系统默认启动画面。 默认情况下,此系统默认启动画面由应用的启动器图标元素和主题的 windowBackground(如果是单色)构成
Android Developer | 将现有的启动画面实现迁移到 Android 12 及更高版本
限制关闭系统对话框
从 Android 12 开始,弃用了 ACTION_CLOSE_SYSTEM_DIALOGS操作。除了一些特殊情况之外,当应用尝试调用包含此操作的 intent 时,系统会基于应用的目标 SDK 版本执行以下操作之一:
- 如果应用以 Android 12 或更高版本为目标平台,则会发生 SecurityException。
- 如果应用以 Android 11(API 级别 30)或更低版本为目标平台,则系统不会执行 intent,并且 Logcat 中会显示警告
根Activity返回键不再Finish
当在根Activity时,按下“返回”按钮不再执行Activity.finishi,而是直接退到后台
有些应用会实现双击返回结束activity功能,这时候需要考虑维持系统行为或者换成finish结束
Android 13.0 (33)
Target 33
发送通知需要动态权限
在Android 13上,发送通知需要申请POST_NOTIFICATIONS权限
如果用户在搭载 Android 13 或更高版本的设备上安装您的应用,应用的通知默认处于关闭状态。在您请求新的权限且用户向您的应用授予该权限之前,您的应用都将无法发送通知。
权限对话框的显示时间取决于应用的target sdk版本:
- >=33,应用将可以完全自行控制权限对话框的显示时间。您可以借此机会向用户说明应用需要此权限的原因,进而鼓励他们授予该权限。
- <=32,在您创建通知渠道后您的应用首次启动 activity 时,或在您的应用启动一个 activity,然后创建它的第一个通知渠道时,系统会显示该权限对话框。这通常是在应用启动时。
Android 14.0 (34)
Target 34
指定前台服务类型
必须为应用内的每个前台服务指定至少一种前台服务类型。
对隐式意图的限制
对于面向 Android 14 的应用,Android 通过以下方式限制应用向内部应用组件发送隐式意图:
- 隐式意图仅传递给导出(exported=true)的组件。
- 如果应用程序创建一个可变的PendingIntent,但Intent未指定组件或包,系统现在会抛出异常。
这些更改可防止恶意应用程序拦截旨在供应用程序内部组件使用的隐式意图。
例如,这是一个声明的Intent Filter:
<activity
android:name=".AppActivity"
android:exported="false">
<intent-filter>
<action android:name="com.example.action.APP_ACTION" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
如果尝试使用隐式意图启动此活动,则会抛出异常:
// Throws an exception when targeting Android 14.
context.startActivity(Intent("com.example.action.APP_ACTION"))
要启动未导出的 Activity,应改用显式 Intent:
// This makes the intent explicit.
val explicitIntent =
Intent("com.example.action.APP_ACTION")
explicitIntent.apply {
package = context.packageName
}
context.startActivity(explicitIntent)
All
限制安装apk
当apk的targetversion<23时,无法安装