和先前版本一樣,Android 14 也包含可能會影響應用程式的行為變更。以下行為變更僅適用於指定 Android 14 (API 級別 34) 以上版本的應用程式。如果您的應用程式指定 Android 14 以上版本,建議您視情況修改應用程式,以支援這些行為。
此外,無論應用程式的 targetSdkVersion
為何,請務必查看影響所有在 Android 14 上執行的應用程式行為變更清單。
核心功能
必須提供前景服務類型
如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须为应用中的每项前台服务指定至少一个前台服务类型。您应该选择一个能够代表应用用例的前台服务类型。系统需要特定类型的前台服务满足特定用例。
如果应用中的用例与这些类型均不相关,强烈建议您迁移逻辑以使用 WorkManager 或用户发起的数据传输作业。
在 BluetoothAdapter 中強制執行 BLUETOOTH_CONNECT 權限
Android 14 enforces the BLUETOOTH_CONNECT
permission when calling the
BluetoothAdapter
getProfileConnectionState()
method for apps targeting
Android 14 (API level 34) or higher.
This method already required the BLUETOOTH_CONNECT
permission, but it was not
enforced. Make sure your app declares BLUETOOTH_CONNECT
in your app's
AndroidManifest.xml
file as shown in the following snippet and check that
a user has granted the permission before calling
getProfileConnectionState
.
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
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 問題:在某些情況下,新增
java.lang.ClassValue
類別後,當您嘗試使用 ProGuard 縮減、模糊處理及最佳化應用程式時,可能會出現問題。問題出在 Kotlin 程式庫,因為此程式庫會根據Class.forName("java.lang.ClassValue")
是否傳回類別來變更執行階段的行為。如果您的應用程式是在缺乏java.lang.ClassValue
類別的情況下,根據舊版執行階段開發,則這些最佳化做法可能會在類別中移除衍生自java.lang.ClassValue
的computeValue
方法。
JobScheduler 強化回呼和網路行為
Since its introduction, JobScheduler expects your app to return from
onStartJob
or onStopJob
within a few seconds. Prior to Android 14,
if a job runs too long, the job is stopped and fails silently.
If your app targets Android 14 (API level 34) or higher and
exceeds the granted time on the main thread, the app triggers an ANR
with the error message "No response to onStartJob
" or
"No response to onStopJob
".
This ANR may be a result of 2 scenarios:
1. There is work blocking the main thread, preventing the callbacks onStartJob
or onStopJob
from executing and completing within the expected time limit.
2. The developer is running blocking work within the JobScheduler
callback onStartJob
or onStopJob
, preventing the callback from
completing within the expected time limit.
To address #1, you will need to further debug what is blocking the main thread
when the ANR occurs, you can do this using
ApplicationExitInfo#getTraceInputStream()
to get the tombstone
trace when the ANR occurs. If you're able to manually reproduce the ANR,
you can record a system trace and inspect the trace using either
Android Studio or Perfetto to better understand what is running on
the main thread when the ANR occurs.
Note that this can happen when using JobScheduler API directly
or using the androidx library WorkManager.
To address #2, consider migrating to WorkManager, which provides
support for wrapping any processing in onStartJob
or onStopJob
in an asynchronous thread.
JobScheduler
also introduces a requirement to declare the
ACCESS_NETWORK_STATE
permission if using setRequiredNetworkType
or
setRequiredNetwork
constraint. If your app does not declare the
ACCESS_NETWORK_STATE
permission when scheduling the job and is targeting
Android 14 or higher, it will result in a SecurityException
.
資訊方塊啟動 API
針對指定 14 以上版本為目標的應用程式,TileService#startActivityAndCollapse(Intent)
已遭淘汰,現在呼叫時會擲回例外狀況。如果您的應用程式從資訊方塊啟動活動,請使用
TileService#startActivityAndCollapse(PendingIntent)
。
隱私權
相片和影片的部分存取權
Android 14 引入了所选照片访问权限,可让用户授权应用访问其媒体库中的特定图片和视频,而不是授予对指定类型的所有媒体的访问权限。
仅当您的应用以 Android 14(API 级别 34)或更高版本为目标平台时,才会启用此变更。如果您还没有使用照片选择器,建议您在应用中实现该选择器,以便在选择图片和视频时提供一致的体验,同时还可以加强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要完全控制您的实现,请调整您的实现,以使用新的 READ_MEDIA_VISUAL_USER_SELECTED
权限。如果您的应用不使用新权限,系统会在兼容模式下运行应用。
使用者體驗
安全的全螢幕意圖通知
With Android 11 (API level 30), it was possible for any app to use
Notification.Builder.setFullScreenIntent
to send full-screen
intents while the phone is locked. You could auto-grant this on app install by
declaring USE_FULL_SCREEN_INTENT
permission in the
AndroidManifest.
Full-screen intent notifications are designed for extremely high-priority
notifications demanding the user's immediate attention, such as an incoming
phone call or alarm clock settings configured by the user. For apps targeting
Android 14 (API level 34) or higher, apps that are allowed to use this
permission are limited to those that provide calling and alarms only. The Google
Play Store revokes default USE_FULL_SCREEN_INTENT
permissions for any apps
that don't fit this profile. The deadline for these policy changes is May 31,
2024.
This permission remains enabled for apps installed on the phone before the user updates to Android 14. Users can turn this permission on and off.
You can use the new API
NotificationManager.canUseFullScreenIntent
to check if your app
has the permission; if not, your app can use the new intent
ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
to launch the settings
page where users can grant the permission.
安全性
隱含和待處理意圖的限制
对于以 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);
已註冊執行階段的廣播接收器必須指定匯出行為
以 Android 14(API 级别 34)或更高版本为目标平台并使用上下文注册的接收器的应用和服务必须指定一个标志,以指明接收器是否应导出到设备上的所有其他应用:分别为 RECEIVER_EXPORTED
或 RECEIVER_NOT_EXPORTED
。此要求有助于利用 Android 13 中引入的这些接收器的功能,保护应用免受安全漏洞的影响。
仅接收系统广播的接收器的例外情况
如果您的应用仅通过 Context#registerReceiver
方法(例如 Context#registerReceiver()
)为系统广播注册接收器,那么在注册接收器时不应指定标志。
更安全的動態程式碼載入
如果您的應用程式指定 Android 14 (API 級別 34) 以上版本,並且使用動態程式碼載入 (DCL),則所有動態載入的檔案都必須標示為唯讀。否則系統會擲回例外狀況。我們建議應用程式盡量避免使用動態載入程式碼,否則應用程式很可能會因為程式碼插入或竄改程式碼而無法正常運作。
如果您必須動態載入程式碼,請使用下列設定方法,在開啟檔案時和寫入任何內容之前,將動態載入的檔案 (例如 DEX、JAR 或 APK 檔案) 設定為唯讀:
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);
處理已存在的動態載入檔案
為避免現有的動態載入檔案擲回例外狀況,建議您先刪除並重新建立檔案,然後再嘗試在應用程式中動態載入這些檔案。重新建立檔案時,請按照上述指示,在寫入時將檔案標記為唯讀。或者,您也可以將現有檔案重新標示為唯讀,但在此類情況下,我們強烈建議您先驗證檔案的完整性 (例如,檢查檔案的簽名與受信任的值是否一致),以避免應用程式遭到惡意攻擊。
從背景啟動活動的額外限制
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,系统会进一步限制允许应用在后台启动 activity 的时间:
- 现在,当应用使用
PendingIntent#send()
或类似方法发送PendingIntent
时,如果它想要授予自己的后台 activity 启动待处理 intent 的启动特权,则必须选择启用。如需选择启用,应用应通过setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
传递ActivityOptions
软件包。 - 当可见应用绑定了另一个后台应用的服务时
bindService()
方法,可见应用现在必须 如果它想要将自己的后台活动 绑定服务。要选择启用,应用应包含BIND_ALLOW_ACTIVITY_STARTS
标志,在调用bindService()
方法。
这些更改扩大了一组现有限制条件的范围,目的是防止恶意应用滥用 API 以在后台启动干扰性活动,从而保护用户。
Zip Path Traversal
針對指定 Android 14 (API 級別 34) 以上版本的應用程式,如果 ZIP 檔案的項目名稱包含「..」或開頭為「/」,Android 會透過下列方式避免 Zip Path Traversal 安全漏洞:ZipFile(String)
和 ZipInputStream.getNextEntry()
會擲回 ZipException
。
應用程式可透過呼叫 dalvik.system.ZipPathValidator.clearCallback()
,選擇不採用此驗證。
每個 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
。
更新非 SDK 限制
基於與 Android 開發人員合作及最新的內部測試,Android 14 包含更新後的受限制非 SDK 介面清單。在限制非 SDK 介面之前,我們盡可能確保公開替代方案的可得性。
如果您的應用程式並非以 Android 14 為目標版本,則此處所述的某些變更可能不會立即對您造成影響。雖然您目前可以使用某些非 SDK 介面 (視應用程式的目標 API 級別而定),但使用任何非 SDK 方法或欄位時,均可能面臨應用程式故障的高度風險。
如果不確定應用程式是否使用非 SDK 介面,可對應用程式進行測試以便確認。如果您的應用程式仰賴非 SDK 介面,則建議您開始規劃遷移至 SDK 替代方案。不過,我們瞭解有些應用程式可使用非 SDK 介面運作。如果您除了為應用程式中的某個功能使用非 SDK 介面外,已別無他法,則應要求新的公用 API。
如需详细了解此 Android 版本中的变更,请参阅 Android 14 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。