Comme les versions précédentes, Android 14 apporte des modifications de comportement pouvant affecter votre application. Les modifications de comportement suivantes s'appliquent exclusivement aux applications qui ciblent Android 14 (niveau d'API 34) ou version ultérieure. Si votre application cible Android 14 ou une version ultérieure, vous devez la modifier pour qu'elle prenne en charge ces comportements, le cas échéant.
Veillez également à consulter la liste des modifications de comportement qui affectent toutes les applications exécutées sur Android 14, peu importe la targetSdkVersion
de l'application.
Fonctionnalité de base
Le type de service au premier plan doit être indiqué
如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须为应用中的每个前台服务至少指定一项前台服务类型。您应选择一个能代表应用用例的前台服务类型。系统需要特定类型的前台服务满足特定用例。
如果应用中的用例与这些类型均不相关,强烈建议您迁移逻辑以使用 WorkManager 或用户发起的数据传输作业。
Application de l'autorisation BLUETOOTH_CONNECT dans BluetoothAdapter
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,在调用 BluetoothAdapter
getProfileConnectionState()
方法时,Android 14 会强制执行 BLUETOOTH_CONNECT
权限。
此方法已需要 BLUETOOTH_CONNECT
权限,但未强制执行。确保您的应用在应用的 AndroidManifest.xml
文件中声明 BLUETOOTH_CONNECT
,如以下代码段所示,并在调用 getProfileConnectionState
之前检查用户是否已授予相应权限。
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Mises à jour OpenJDK 17
Android 14 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致,包括适合应用和平台开发者的库更新和 Java 17 语言支持。
以下变更可能会影响应用兼容性:
- 对正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到
java.util.regex.Matcher
类抛出IllegalArgumentException
的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换DISALLOW_INVALID_GROUP_REFERENCE
标志。 - UUID 处理:现在,验证输入参数时,
java.util.UUID.fromString()
方法会执行更严格的检查,因此您可能会在反序列化期间看到IllegalArgumentException
。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换ENABLE_STRICT_VALIDATION
标志。 - ProGuard 问题:有时,在您尝试使用 ProGuard 缩减、混淆和优化应用时,添加
java.lang.ClassValue
类会导致问题。问题源自 Kotlin 库,该库会根据Class.forName("java.lang.ClassValue")
是否会返回类更改运行时行为。如果您的应用是根据没有java.lang.ClassValue
类的旧版运行时开发的,则这些优化可能会将computeValue
方法从派生自java.lang.ClassValue
的类中移除。
JobScheduler renforce le comportement des rappels et du réseau
自从引入后,JobScheduler 期望您的应用从
onStartJob
或 onStopJob
。在 Android 14 之前,如果作业运行时间过长,系统会停止作业并静默失败。如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,
超过在主线程上授予的时间,应用会触发 ANR
显示“没有响应 onStartJob
”错误消息或
“onStopJob
没有回复”。
此 ANR 可能是由以下 2 种情况造成的:
1.有工作阻塞主线程,阻止回调 onStartJob
或者onStopJob
在预期时间内执行并完成。
2. 开发者在 JobScheduler 中运行阻塞工作
回调 onStartJob
或 onStopJob
,阻止从
在预期的时限内完成
要解决第 1 个问题,您需要进一步调试阻塞主线程的因素
您可以使用以下代码
ApplicationExitInfo#getTraceInputStream()
,用于获取 Tombstone
ANR 发生时的跟踪信息如果您能够手动重现 ANR 问题
您可以录制系统轨迹,并使用
Android Studio 或 Perfetto,以便更好地了解应用上运行的
在发生 ANR 时调用主线程
请注意,直接使用 JobScheduler API 或使用 androidx 库 WorkManager 时可能会发生这种情况。
如需解决问题 2,请考虑迁移到 WorkManager,它支持将 onStartJob
或 onStopJob
中的任何处理封装在异步线程中。
JobScheduler
还引入了一项要求,即如果使用 setRequiredNetworkType
或 setRequiredNetwork
约束条件,则必须声明 ACCESS_NETWORK_STATE
权限。如果您的应用未声明
ACCESS_NETWORK_STATE
权限
Android 14 或更高版本,则会导致 SecurityException
。
API de lancement des tuiles
对于以 Android 14 及更高版本为目标平台的应用,
TileService#startActivityAndCollapse(Intent)
已弃用,现在会抛出
调用时抛出异常。如果您的应用从功能块启动 activity,请使用
TileService#startActivityAndCollapse(PendingIntent)
。
Confidentialité
Accès limité aux photos et aux vidéos
Android 14 引入了“已选照片访问权限”,让用户可以向应用授予对其媒体库中特定图片和视频的访问权限,而不是授予对给定类型的所有媒体的访问权限。
只有当您的应用以 Android 14(API 级别 34)或更高版本为目标平台时,此更改才会启用。如果您尚未使用照片选择器,我们建议您在应用中实现照片选择器,以提供一致的图片和视频选择体验,同时增强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要对实现保持完全控制,请调整实现以使用新的 READ_MEDIA_VISUAL_USER_SELECTED
权限。如果您的应用不使用新权限,系统会以兼容模式运行您的应用。
Expérience utilisateur
Notifications d'intent plein écran sécurisées
Avec Android 11 (niveau d'API 30), toutes les applications pouvaient utiliser Notification.Builder.setFullScreenIntent
pour envoyer des intents plein écran lorsque le téléphone était verrouillé. Vous pouvez l'accorder automatiquement lors de l'installation de l'application en déclarant l'autorisation USE_FULL_SCREEN_INTENT
dans le fichier AndroidManifest.
Les notifications d'intent plein écran sont conçues pour les notifications à priorité très élevée nécessitant l'attention immédiate de l'utilisateur, comme les appels entrants ou les paramètres d'alarme configurés par l'utilisateur. Pour les applications ciblant Android 14 (niveau d'API 34) ou version ultérieure, seules les applications qui proposent des fonctionnalités d'appel et d'alarmes peuvent utiliser cette autorisation. Le Google Play Store révoque les autorisations USE_FULL_SCREEN_INTENT
par défaut pour toutes les applications qui ne correspondent pas à ce profil. La date limite pour appliquer ces modifications est le 31 mai 2024.
Cette autorisation reste activée pour les applications installées sur le téléphone avant que l'utilisateur passe à Android 14. Les utilisateurs peuvent activer et désactiver cette autorisation.
Vous pouvez utiliser la nouvelle API NotificationManager.canUseFullScreenIntent
pour vérifier si votre application dispose de l'autorisation. Sinon, votre application peut utiliser le nouvel intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
pour afficher la page des paramètres où les utilisateurs peuvent accorder l'autorisation.
Sécurité
Restrictions concernant les intents implicites et en attente
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式限制应用向内部应用组件发送隐式 intent:
- 隐式 intent 只能传送到导出的组件。应用必须使用显式 intent 传送到未导出的组件,或将该组件标记为已导出。
- 如果应用通过未指定组件或软件包的 intent 创建可变待处理 intent,系统会抛出异常。
这些变更可防止恶意应用拦截意在供应用内部组件使用的隐式 intent。
例如,下面是可以在应用的清单文件中声明的 intent 过滤器:
<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>
如果应用尝试使用隐式 intent 启动此 activity,则系统会抛出 ActivityNotFoundException
异常:
Kotlin
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(Intent("com.example.action.APP_ACTION"))
Java
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
如需启动非导出的 activity,应用应改用显式 intent:
Kotlin
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
Java
// This makes the intent explicit. Intent explicitIntent = new Intent("com.example.action.APP_ACTION") explicitIntent.setPackage(context.getPackageName()); context.startActivity(explicitIntent);
Les broadcasts receivers enregistrés lors de l'exécution doivent spécifier le comportement d'exportation
Les applications et les services qui ciblent Android 14 (niveau d'API 34) ou version ultérieure et utilisent des récepteurs enregistrés en contexte doivent spécifier un indicateur pour indiquer si le récepteur doit être, ou non, exporté vers toutes les autres applications de l'appareil: RECEIVER_EXPORTED
ou RECEIVER_NOT_EXPORTED
, respectivement.
Cette exigence permet de protéger les applications contre les failles de sécurité en tirant parti des fonctionnalités pour ces récepteurs introduites dans Android 13.
Exception pour les récepteurs qui ne reçoivent que des annonces du système
Si votre application n'enregistre un récepteur que pour les annonces du système via les méthodes Context#registerReceiver
, telles que Context#registerReceiver()
, aucun indicateur ne doit être spécifié lors de l'enregistrement du récepteur.
Chargement de code dynamique plus sécurisé
Si votre application cible Android 14 (niveau d'API 34) ou une version ultérieure et utilise le chargement dynamique du code (DCL), tous les fichiers chargés dynamiquement doivent être marqués en lecture seule. Sinon, le système génère une exception. Nous recommandons aux applications d'éviter à charger dynamiquement du code autant que possible, car cela augmente considérablement le risque qu'une application compromises par une injection ou une falsification de code.
Si vous devez charger le code de manière dynamique, utilisez l'approche suivante pour définir le fichier chargé dynamiquement (tel qu'un fichier DEX, JAR ou APK) en lecture seule dès que le fichier est ouvert et avant toute écriture :
Kotlin
val jar = File("DYNAMICALLY_LOADED_FILE.jar") val os = FileOutputStream(jar) os.use { // Set the file to read-only first to prevent race conditions jar.setReadOnly() // Then write the actual file content } val cl = PathClassLoader(jar, parentClassLoader)
Java
File jar = new File("DYNAMICALLY_LOADED_FILE.jar"); try (FileOutputStream os = new FileOutputStream(jar)) { // Set the file to read-only first to prevent race conditions jar.setReadOnly(); // Then write the actual file content } catch (IOException e) { ... } PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);
Gérer les fichiers chargés dynamiquement préexistants
Pour éviter de générer des exceptions pour les fichiers existants chargés dynamiquement, nous vous recommandons de supprimer et de recréer les fichiers avant de réessayer de les charger dynamiquement dans votre application. Lorsque vous recréez les fichiers, suivez les instructions précédentes pour les marquer en lecture seule au moment de l'écriture. Vous pouvez également libeller à nouveau les fichiers existants en lecture seule, mais dans ce cas, nous vous recommandons vivement de vérifier d'abord l'intégrité des fichiers (par exemple, en vérifiant la signature du fichier correspond à une valeur de confiance), pour protéger votre application des actions malveillantes.
Restrictions supplémentaires concernant le démarrage d'activités en arrière-plan
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,系统会进一步限制允许应用在后台启动 activity 的时间:
- 现在,当应用使用
PendingIntent#send()
或类似方法发送PendingIntent
时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。如需选择启用,应用应通过setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
传递ActivityOptions
软件包。 - 当可见应用使用
bindService()
方法绑定其他在后台应用的服务时,如果可见应用想要授予自己的后台 activity 对绑定服务的启动特权,则必须选择启用。如需选择启用,应用应在调用bindService()
方法时包含BIND_ALLOW_ACTIVITY_STARTS
标志。
这些更改扩大了一组现有限制条件的范围,目的是防止恶意应用滥用 API 以在后台启动干扰性活动,从而保护用户。
Traversée de répertoire ZIP
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式防止 Zip 路径遍历漏洞:如果 Zip 文件条目名称包含“..”或以“/”开头,ZipFile(String)
和 ZipInputStream.getNextEntry()
会抛出 ZipException
。
应用可以通过调用 dalvik.system.ZipPathValidator.clearCallback()
选择停用此验证。
Consentement de l'utilisateur requis pour chaque session de capture MediaProjection
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,在以下任一情况下,MediaProjection#createVirtualDisplay
都会抛出 SecurityException
:
- 您的应用会缓存从
MediaProjectionManager#createScreenCaptureIntent
返回的Intent
,并多次将其传递给MediaProjectionManager#getMediaProjection
。 - 您的应用在同一
MediaProjection
实例上多次调用MediaProjection#createVirtualDisplay
。
您的应用必须在每次捕获会话之前征求用户同意。单次捕获会话是对 MediaProjection#createVirtualDisplay
的单次调用,并且每个 MediaProjection
实例只能使用一次。
处理配置变更
如果您的应用需要调用 MediaProjection#createVirtualDisplay
来处理配置更改(例如屏幕方向或屏幕大小更改),您可以按照以下步骤更新现有 MediaProjection
实例的 VirtualDisplay
:
- 使用新的宽度和高度调用
VirtualDisplay#resize
。 - 向
VirtualDisplay#setSurface
提供新的Surface
,并为其指定新的宽度和高度。
注册回调
您的应用应注册回调,以处理用户不同意继续拍摄会话的情况。为此,请实现 Callback#onStop
,并让应用释放所有相关资源(例如 VirtualDisplay
和 Surface
)。
如果您的应用未注册此回调,当您的应用调用它时,MediaProjection#createVirtualDisplay
会抛出 IllegalStateException
。
Mise à jour des restrictions non SDK
Android 14 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。
如果您的应用并非以 Android 14 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API。
Pour en savoir plus sur les modifications apportées à cette version d'Android, consultez la section Mises à jour des restrictions d'interface non SDK dans Android 14. Pour en savoir plus sur les interfaces non SDK en général, consultez la section Restrictions concernant les interfaces non SDK.