和先前版本一樣,Android 14 也包含可能會影響應用程式的行為變更。以下行為變更僅適用於指定 Android 14 (API 級別 34) 以上版本的應用程式。如果您的應用程式指定 Android 14 以上版本,建議您視情況修改應用程式,以支援這些行為。
此外,無論應用程式的 targetSdkVersion 為何,請務必查看對所有 Android 14 應用程式有影響的行為變更清單。
核心功能
必須提供前景服務類型
如果您的應用程式以 Android 14 (API 級別 34) 以上版本為目標,則必須為應用程式中的每個前景服務指定至少一個前景服務類型。您應選擇代表應用程式用途的前景服務類型。系統會預期具有特定類型的前景服務,以滿足特定用途。
如果應用程式的用途與上述任一類型無關,強烈建議您遷移邏輯,以便使用 WorkManager 或使用者啟動的資料移轉作業。
在 BluetoothAdapter 中強制執行 BLUETOOTH_CONNECT 權限
对于以 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" />
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 會強化回呼和網路行為
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 权限。如果您的应用不使用新权限,系统会以兼容模式运行您的应用。
使用者體驗
安全的全螢幕意圖通知
在 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 启动设置页面,在该页面中,用户可以授予权限。
安全性
隱含和待處理意圖的限制
針對以 Android 14 (API 級別 34) 以上版本為目標的應用程式,Android 會透過以下方式,限制應用程式傳送隱含意圖至內部應用程式元件:
- 隱含意圖只會傳送至匯出的元件。應用程式必須使用明確意圖傳送至未匯出的元件,或將元件標示為已匯出。
- 如果應用程式建立可變動的待處理意圖,且意圖未指定元件或套件,系統會擲回例外狀況。
這些異動可防止惡意應用程式攔截由應用程式內部元件使用的隱含意圖。
例如,以下是可在應用程式資訊清單檔案中宣告的意圖篩選器:
<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>
如果您的應用程式嘗試以隱含意圖啟動這個活動,系統會擲回 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"));
若要啟動非匯出活動,應用程式應改用明確意圖:
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);
已註冊執行階段的廣播接收器必須指定匯出行為
Apps and services that target Android 14 (API level 34) or higher and use
context-registered receivers are required to specify a flag
to indicate whether or not the receiver should be exported to all other apps on
the device: either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED, respectively.
This requirement helps protect apps from security vulnerabilities by leveraging
the features for these receivers introduced in Android 13.
Exception for receivers that receive only system broadcasts
If your app is registering a receiver only for
system broadcasts through Context#registerReceiver
methods, such as Context#registerReceiver(), then it
shouldn't specify a flag when registering the receiver.
更安全的動態程式碼載入
如果您的應用程式指定 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) 以上版本為目標版本的應用程式,系統會進一步限制應用程式可從背景啟動活動的情況:
- 當應用程式使用
PendingIntent#send()或類似方法傳送PendingIntent時,如果應用程式要自行授予背景活動啟動權限,以便啟動待處理意圖,則須在當前階段選擇加入。如要選擇加入,應用程式應透過setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)傳遞ActivityOptions組合。 - 如果開放瀏覽權限的應用程式使用
bindService()方法,在背景中綁定另一個應用程式的服務,則開放瀏覽權限的應用程式必須在當前階段選擇加入,才可將其背景活動啟動權限授予該綁定的服務。如要選擇加入,應用程式在呼叫bindService()方法時,應納入BIND_ALLOW_ACTIVITY_STARTS標記。
這些變更會擴大現有的限制組合,避免惡意應用程式濫用 API 而從背景啟動干擾活動,進而為使用者提供保護。
Zip Path Traversal
針對指定 Android 14 (API 級別 34) 以上版本的應用程式,Android 會透過下列方式避免 Zip Path Traversal 安全漏洞:如果 ZIP 檔案的項目名稱包含「..」或以「/」開頭,則 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 介面的限制」。