Como nas versões anteriores, o Android 14 inclui mudanças de comportamento que podem afetar seu app. As seguintes mudanças de comportamento se aplicam exclusivamente a apps destinados ao Android 14 (nível 34 da API) ou mais recente. Caso seu app seja direcionado ao Android 14 ou a versões mais recentes, faça modificações para oferecer suporte a esses comportamentos de forma adequada, quando aplicável.
Consulte também a lista de mudanças de comportamento que afetam todos os apps
executados no Android 14, independente da
targetSdkVersion
do app.
Principal recurso
Os tipos de serviço em primeiro plano são obrigatórios
Se o app for destinado ao Android 14 (nível 34 da API) ou mais recente, ele precisará especificar pelo menos um tipo de serviço em primeiro plano para cada serviço em primeiro plano. Escolha um tipo que represente o caso de uso do app. O sistema espera que serviços em primeiro plano com um tipo específico satisfaça um caso de uso específico.
Se um caso de uso no app não estiver associado a nenhum desses tipos, é recomendável migrar a lógica para usar o WorkManager ou jobs de transferência de dados iniciados pelo usuário.
Aplicação da permissão BLUETOOTH_CONNECT no BluetoothAdapter
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 14 会在调用 BluetoothAdapter
getProfileConnectionState()
方法时强制执行 BLUETOOTH_CONNECT
权限。
此方法已要求 BLUETOOTH_CONNECT
权限,但未强制执行。确保您的应用在应用的 AndroidManifest.xml
文件中声明 BLUETOOTH_CONNECT
,如以下代码段所示,并在调用 getProfileConnectionState
之前检查用户是否已授予相应权限。
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Atualizações do 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
的类中移除。
O JobScheduler reforça o callback e o comportamento da rede
Desde a introdução, o JobScheduler espera que o app retorne de
onStartJob
ou onStopJob
em alguns segundos. Antes do Android 14,
se um job for executado por muito tempo, ele será interrompido e falhará silenciosamente.
Caso o app seja direcionado ao Android 14 (nível 34 da API) ou versões mais recentes e
exceder o tempo concedido na linha de execução principal, o app acionará um ANR
com a mensagem de erro "Sem resposta para onStartJob
" ou
"Sem resposta para onStopJob
".
Esse ANR pode ocorrer devido a dois cenários:
1: Há trabalho bloqueando a linha de execução principal, impedindo que os callbacks onStartJob
ou onStopJob
sejam executados e concluídos dentro do limite de tempo esperado.
2. O desenvolvedor está executando um trabalho de bloqueio no JobScheduler
callback onStartJob
ou onStopJob
, impedindo que ele seja
para serem concluídas dentro do limite de tempo esperado.
Para resolver o problema no 1, é necessário depurar melhor o que está bloqueando a linha de execução principal
quando o ANR ocorrer,
ApplicationExitInfo#getTraceInputStream()
para acessar a tombstone
rastrear quando o ANR ocorrer. Se você conseguir reproduzir o ANR manualmente,
você pode gravar um rastro do sistema e inspecioná-lo usando
Android Studio ou Perfetto para entender melhor o que está sendo executado no
na linha de execução principal quando o ANR ocorre.
Isso pode acontecer ao usar a API JobScheduler diretamente
ou a biblioteca androidx WorkManager.
Para resolver o problema 2, considere migrar para o WorkManager, que oferece
suporte para encapsulamento de qualquer processamento em onStartJob
ou onStopJob
em uma linha de execução assíncrona.
JobScheduler
também introduz um requisito para declarar o
Permissão ACCESS_NETWORK_STATE
se você estiver usando setRequiredNetworkType
ou
setRequiredNetwork
restrição. Caso o app não declare a
Permissão ACCESS_NETWORK_STATE
ao programar o job e estiver segmentando
Android 14 ou mais recente, isso vai gerar uma SecurityException
.
API de inicialização de blocos
对于以 Android 14 及更高版本为目标平台的应用,
TileService#startActivityAndCollapse(Intent)
已弃用,现在会抛出
调用时抛出异常。如果您的应用从功能块启动 activity,请使用
TileService#startActivityAndCollapse(PendingIntent)
。
Privacidade
Acesso parcial a fotos e vídeos
Android 14 引入了“已选照片访问权限”,让用户可以向应用授予对其媒体库中特定图片和视频的访问权限,而不是授予对给定类型的所有媒体的访问权限。
只有当您的应用以 Android 14(API 级别 34)或更高版本为目标平台时,此更改才会启用。如果您尚未使用照片选择器,我们建议您在应用中实现照片选择器,以提供一致的图片和视频选择体验,同时增强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要对实现保持完全控制,请调整实现以使用新的 READ_MEDIA_VISUAL_USER_SELECTED
权限。如果您的应用不使用新权限,系统会以兼容模式运行您的应用。
Experiência do usuário
Notificações seguras de intent de tela cheia
在 Android 11(API 级别 30)中,任何应用都可以在手机处于锁定状态时使用 Notification.Builder.setFullScreenIntent
发送全屏 intent。您可以通过在 AndroidManifest 中声明 USE_FULL_SCREEN_INTENT
权限,在应用安装时自动授予此权限。
全屏 intent 通知适用于需要用户立即注意的极高优先级通知,例如用户来电或用户配置的闹钟设置。对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,获准使用此权限的应用仅限于提供通话和闹钟的应用。对于不适合此情况的任何应用,Google Play 商店会撤消其默认的 USE_FULL_SCREEN_INTENT
权限。这些政策变更的截止日期为 2024 年 5 月 31 日。
在用户更新到 Android 14 之前,在手机上安装的应用仍拥有此权限。用户可以开启和关闭此权限。
您可以使用新 API NotificationManager.canUseFullScreenIntent
检查应用是否具有该权限;如果没有,应用可以使用新 intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
启动设置页面,在该页面中,用户可以授予权限。
Segurança
Restrições a intents implícitas e pendentes
Para apps destinados ao Android 14 (nível 34 da API) ou mais recentes, o Android impede que intents implícitas sejam enviadas para componentes internos do app das seguintes maneiras:
- Intents implícitas são entregues apenas a componentes exportados. Os apps precisam usar uma intent explícita para enviar para componentes não exportados ou marcar o componente como exportado.
- Se um app criar uma intent pendente mutável com uma intent que não especifica um componente ou pacote, o sistema vai gerar uma exceção.
Essas mudanças impedem que apps maliciosos interceptem intents implícitas destinadas ao uso de componentes internos de um app.
Confira um filtro de intent que pode ser declarado no arquivo de manifesto do app:
<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>
Se o app tentar iniciar essa atividade usando uma intent implícita, uma
exceção ActivityNotFoundException
será gerada:
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"));
Para iniciar a atividade não exportada, o app precisa usar uma intent explícita:
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);
Os broadcast receivers registrados no ambiente de execução precisam especificar o comportamento de exportação
以 Android 14(API 级别 34)或更高版本为目标平台并使用上下文注册的接收器的应用和服务必须指定以下标志,以指明接收器是否应导出到设备上的所有其他应用:RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。此要求有助于利用 Android 13 中引入的这些接收器的功能,来保护应用免受安全漏洞的影响。
仅接收系统广播的接收器的例外情况
如果您的应用仅通过 Context#registerReceiver
方法(例如 Context#registerReceiver()
)针对系统广播注册接收器,那么它在注册接收器时不应指定标志。
Carregamento mais seguro de código dinâmico
Se o app for direcionado ao Android 14 (nível 34 da API) ou mais recente e usar o carregamento de código dinâmico (DCL, na sigla em inglês), todos os arquivos carregados dinamicamente precisarão ser marcados como somente leitura. Caso contrário, o sistema vai gerar uma exceção. Recomendamos que os apps evitem carregar código dinamicamente sempre que possível, porque isso aumenta muito o risco de comprometimento do app por injeção ou adulteração de código.
Se você precisar carregar código dinamicamente, use a seguinte abordagem para definir o arquivo carregado dessa forma (como um arquivo DEX, JAR ou APK) como somente leitura assim que ele for aberto e antes de qualquer conteúdo ser programado:
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);
Processar arquivos carregados dinamicamente que já existem
Para evitar que exceções sejam geradas para arquivos carregados dinamicamente já existentes, recomendamos excluir e recriar os arquivos antes de tentar carregá-los dessa forma no app. Ao recriar os arquivos, siga as orientações anteriores para marcá-los como somente leitura no momento da gravação. Como alternativa, rotule novamente os arquivos existentes como somente leitura, mas, nesse caso, recomendamos verificar a integridade dos arquivos primeiro, comparando a assinatura do arquivo com um valor confiável, por exemplo, para ajudar a proteger seu app contra ações maliciosas.
Mais restrições para o início de atividades em segundo plano
对于以 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 以在后台启动干扰性活动,从而保护用户。
Travessia de caminhos de arquivo ZIP
Em apps destinados ao Android 14 (nível 34 da API) ou mais recente, o Android evita a vulnerabilidade
da travessia de caminhos de arquivo ZIP da seguinte forma:
ZipFile(String)
e
ZipInputStream.getNextEntry()
geram uma
ZipException
quando os nomes dos arquivos ZIP têm ".." ou começam
com "/".
Os apps podem desativar essa validação chamando
dalvik.system.ZipPathValidator.clearCallback()
.
O consentimento do usuário é necessário para cada sessão de captura de 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
。
Atualização das restrições não SDK
O Android 14 inclui listas atualizadas de interfaces não SDK restritas com base na colaboração com desenvolvedores Android e nos testes internos mais recentes. Antes de restringirmos interfaces não SDK, sempre que possível, garantimos que haja alternativas públicas disponíveis.
Caso seu app não seja destinado ao Android 14, é possível que algumas dessas mudanças não afetem você imediatamente. No entanto, embora atualmente seja possível usar algumas interfaces não SDK (dependendo do nível da API de destino do app), o uso de qualquer método ou campo não SDK sempre apresenta um alto risco de corromper o app.
Se você não sabe se o app usa interfaces não SDK, é possível testá-lo para descobrir. Se ele depende de interfaces não SDK, planeje uma migração para alternativas SDK. No entanto, entendemos que alguns apps têm casos de uso válidos para interfaces não SDK. Se você não encontrar uma alternativa para deixar de usar uma interface não SDK em um recurso no app, solicite uma nova API pública.
如需详细了解此 Android 版本中的变更,请参阅 Android 14 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。