เช่นเดียวกับรุ่นก่อนหน้า Android 14 มีการเปลี่ยนแปลงลักษณะการทำงานที่อาจส่งผลต่อแอปของคุณ การเปลี่ยนแปลงลักษณะการทำงานต่อไปนี้มีผลเฉพาะกับแอปที่กำหนดเป้าหมายเป็น Android 14 (ระดับ API 34) ขึ้นไป หากแอปกำหนดเป้าหมายเป็น Android 14 ขึ้นไป คุณควรแก้ไขแอปให้รองรับลักษณะการทำงานเหล่านี้อย่างเหมาะสม ในกรณีที่เกี่ยวข้อง
อย่าลืมตรวจสอบรายการการเปลี่ยนแปลงลักษณะการทำงานที่มีผลกับแอปทั้งหมด
ที่ทำงานบน Android 14 ไม่ว่าtargetSdkVersion ของแอปจะเป็นอย่างไร
ฟังก์ชันหลัก
ต้องระบุประเภทบริการที่ทำงานอยู่เบื้องหน้า
如果您的应用以 Android 14(API 级别 34)或更高版本为目标平台,则必须为应用中的每个前台服务至少指定一项前台服务类型。您应选择一个能代表应用用例的前台服务类型。系统需要特定类型的前台服务满足特定用例。
如果应用中的用例与这些类型均不相关,强烈建议您迁移逻辑以使用 WorkManager 或用户发起的数据传输作业。
การบังคับใช้สิทธิ์ BLUETOOTH_CONNECT ใน 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" />
การอัปเดต OpenJDK 17
Android 14 ยังคงปรับปรุงไลบรารีหลักของ Android ให้สอดคล้องกับฟีเจอร์ใน OpenJDK LTS เวอร์ชันล่าสุด ซึ่งรวมถึงทั้งการอัปเดตไลบรารีและการรองรับภาษา Java 17 สําหรับนักพัฒนาแอปและแพลตฟอร์ม
การเปลี่ยนแปลงบางอย่างอาจส่งผลต่อความเข้ากันได้ของแอป
- การเปลี่ยนแปลงนิพจน์ทั่วไป: ตอนนี้ระบบไม่อนุญาตให้ใช้การอ้างอิงกลุ่มที่ไม่ถูกต้องเพื่อให้สอดคล้องกับความหมายของ OpenJDK มากขึ้น คุณอาจเห็นกรณีใหม่ซึ่งคลาส
java.util.regex.Matcherแสดงข้อยกเว้นIllegalArgumentExceptionดังนั้นโปรดทดสอบแอปในส่วนที่ใช้นิพจน์ทั่วไป หากต้องการเปิดหรือปิดใช้การเปลี่ยนแปลงนี้ขณะทดสอบ ให้สลับ FlagDISALLOW_INVALID_GROUP_REFERENCEโดยใช้เครื่องมือเฟรมเวิร์กความเข้ากันได้ - การจัดการ UUID: ตอนนี้เมธอด
java.util.UUID.fromString()จะตรวจสอบอย่างเข้มงวดมากขึ้นเมื่อตรวจสอบอาร์กิวเมนต์อินพุต คุณจึงอาจเห็นIllegalArgumentExceptionระหว่างการแปลงข้อมูลย้อนกลับ หากต้องการเปิดหรือปิดใช้การเปลี่ยนแปลงนี้ขณะทดสอบ ให้สลับ FlagENABLE_STRICT_VALIDATIONโดยใช้เครื่องมือเฟรมเวิร์กความเข้ากันได้ - ปัญหาเกี่ยวกับ ProGuard: ในบางกรณี การเพิ่มคลาส
java.lang.ClassValueจะทำให้เกิดปัญหาหากคุณพยายามบีบอัด สร้างความสับสน และเพิ่มประสิทธิภาพแอปโดยใช้ ProGuard ปัญหานี้เกิดจากไลบรารี Kotlin ที่เปลี่ยนลักษณะการทํางานรันไทม์โดยขึ้นอยู่กับว่าClass.forName("java.lang.ClassValue")แสดงผลคลาสหรือไม่ หากแอปของคุณพัฒนาขึ้นโดยใช้รันไทม์เวอร์ชันเก่าที่ไม่มีคลาสjava.lang.ClassValueอยู่ การเพิ่มประสิทธิภาพเหล่านี้อาจนําเมธอดcomputeValueออกจากคลาสที่มาจากjava.lang.ClassValue
JobScheduler เสริมการทำงานของแฮนเดิลการเรียกกลับและลักษณะการทำงานของเครือข่าย
นับตั้งแต่เปิดตัว JobScheduler คาดหวังว่าแอปของคุณจะกลับมาจาก onStartJob หรือ onStopJob ภายในไม่กี่วินาที ก่อนที่จะเป็น Android 14 หากงานทำงานนานเกินไป ระบบจะหยุดงานและดำเนินการไม่สำเร็จโดยอัตโนมัติ
หากแอปกำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป และ
เกินเวลาที่ได้รับในเทรดหลัก แอปทำให้เกิด ANR
ด้วยข้อความแสดงข้อผิดพลาด "ไม่มีการตอบกลับ onStartJob" หรือ
"ไม่ตอบกลับ onStopJob"
ANR นี้อาจเกิดจาก 2 สถานการณ์ ดังนี้
1. มีงานบล็อกเทรดหลัก ซึ่งทําให้ Callback onStartJob หรือ onStopJob ไม่สามารถดําเนินการและทํางานให้เสร็จภายในเวลาจํากัดที่คาดไว้
2. นักพัฒนาแอปกำลังทำงานที่บล็อกภายในการเรียกกลับ onStartJob หรือ onStopJob ของ JobScheduler ซึ่งทำให้การเรียกกลับดำเนินการไม่เสร็จภายในเวลาจำกัดที่คาดไว้
ในการแก้ไขข้อ 1 คุณจะต้องแก้ไขข้อบกพร่องของสิ่งที่บล็อกเทรดหลักเพิ่มเติม
เมื่อเกิด ANR ขึ้น คุณสามารถทำเช่นนี้ได้โดยใช้
ApplicationExitInfo#getTraceInputStream() เพื่อรับ Tombstone
ติดตามเมื่อเกิด ANR หากคุณสร้าง ANR ซ้ำด้วยตนเองได้
คุณสามารถบันทึกการติดตามของระบบและตรวจสอบการติดตามได้โดยใช้
Android Studio หรือ Perfetto เพื่อให้เข้าใจได้ดีขึ้นถึงสิ่งที่กำลังทำงานอยู่
เทรดหลักเมื่อเกิด ANR
โปรดทราบว่าปัญหานี้อาจเกิดขึ้นเมื่อใช้ JobScheduler API โดยตรง
หรือใช้ WorkManager ซึ่งเป็นไลบรารี androidx
หากต้องการแก้ไขปัญหาที่ 2 ให้ลองเปลี่ยนไปใช้ WorkManager ซึ่งรองรับการรวมการประมวลผลใน onStartJob หรือ onStopJob ในเธรดแบบแอซิงโครนัส
JobScheduler ยังกำหนดให้ต้องประกาศสิทธิ์ ACCESS_NETWORK_STATE ด้วยหากใช้ข้อจำกัด setRequiredNetworkType หรือ setRequiredNetwork หากแอปของคุณไม่ได้ประกาศฟิลด์
สิทธิ์ ACCESS_NETWORK_STATE เมื่อกำหนดเวลางานและกำหนดเป้าหมาย
Android 14 ขึ้นไปจะส่งผลให้เกิด SecurityException
API การเปิดตัวไทล์
对于以 Android 14 及更高版本为目标平台的应用,
TileService#startActivityAndCollapse(Intent) 已弃用,现在会抛出
调用时抛出异常。如果您的应用从功能块启动 activity,请使用
TileService#startActivityAndCollapse(PendingIntent)。
ความเป็นส่วนตัว
สิทธิ์เข้าถึงรูปภาพและวิดีโอบางส่วน
Android 14 引入了“已选照片访问权限”,让用户可以向应用授予对其媒体库中特定图片和视频的访问权限,而不是授予对给定类型的所有媒体的访问权限。
只有当您的应用以 Android 14(API 级别 34)或更高版本为目标平台时,此更改才会启用。如果您尚未使用照片选择器,我们建议您在应用中实现照片选择器,以提供一致的图片和视频选择体验,同时增强用户隐私保护,而无需请求任何存储权限。
如果您使用存储权限维护自己的图库选择器,并且需要对实现保持完全控制,请调整实现以使用新的 READ_MEDIA_VISUAL_USER_SELECTED 权限。如果您的应用不使用新权限,系统会以兼容模式运行您的应用。
ประสบการณ์ของผู้ใช้
รักษาความปลอดภัยให้กับการแจ้งเตือน Intent แบบเต็มหน้าจอ
ใน Android 11 (API ระดับ 30) แอปใดก็ได้ที่จะใช้ Notification.Builder.setFullScreenIntent เพื่อส่ง Intent แบบเต็มหน้าจอได้ขณะที่โทรศัพท์ล็อกอยู่ คุณสามารถให้สิทธิ์นี้โดยอัตโนมัติเมื่อติดตั้งแอปโดยประกาศสิทธิ์ USE_FULL_SCREEN_INTENT ใน AndroidManifest
การแจ้งเตือน Intent แบบเต็มหน้าจอออกแบบมาเพื่อแจ้งเตือนที่มีลำดับความสำคัญสูงมากซึ่งต้องการให้ผู้ใช้สนใจในทันที เช่น การโทรเข้าหรือการตั้งค่านาฬิกาปลุกที่ผู้ใช้กำหนดค่าไว้ สำหรับแอปที่กำหนดเป้าหมายเป็น Android 14 (API ระดับ 34) ขึ้นไป แอปที่ได้รับอนุญาตให้ใช้สิทธิ์นี้จะจำกัดไว้เฉพาะแอปที่มีการโทรและการปลุกเท่านั้น Google Play Store จะเพิกถอนสิทธิ์ USE_FULL_SCREEN_INTENT เริ่มต้นสำหรับแอปที่ไม่ตรงกับโปรไฟล์นี้ กำหนดเวลาสำหรับการเปลี่ยนแปลงนโยบายเหล่านี้คือ31 พฤษภาคม 2024
สิทธิ์นี้จะยังคงเปิดใช้อยู่สำหรับแอปที่ติดตั้งในโทรศัพท์ก่อนที่ผู้ใช้จะอัปเดตเป็น Android 14 ผู้ใช้จะเปิดหรือปิดสิทธิ์นี้ได้
คุณสามารถใช้ API ใหม่ NotificationManager.canUseFullScreenIntent เพื่อตรวจสอบว่าแอปของคุณมีสิทธิ์หรือไม่ หากไม่มี แอปจะใช้ Intent ใหม่ ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT เพื่อเปิดหน้าการตั้งค่าที่ผู้ใช้สามารถให้สิทธิ์ได้
ความปลอดภัย
ข้อจำกัดของ Intent โดยนัยและ Intent ที่รอดำเนินการ
对于以 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);
Broadcast Receiver ที่ลงทะเบียนรันไทม์ต้องระบุลักษณะการส่งออก
以 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) ขึ้นไป ระบบจะจำกัดเพิ่มเติมว่าแอปจะได้รับอนุญาตให้เริ่มกิจกรรมจากเบื้องหลังเมื่อใด
- เมื่อแอปส่ง
PendingIntentโดยใช้PendingIntent#send()หรือวิธีการที่คล้ายกัน แอปต้องเลือกใช้หากต้องการมอบสิทธิ์การเริ่มกิจกรรมเบื้องหลังของตนเองเพื่อเริ่ม Intent ที่รอดำเนินการ หากต้องการเลือกใช้ แอปควรส่งActivityOptionsBundle ที่มีsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED) - เมื่อแอปที่มองเห็นได้เชื่อมโยงบริการของแอปอื่นที่อยู่ในเบื้องหลังโดยใช้เมธอด
bindService()ตอนนี้แอปที่มองเห็นได้ต้องเลือกใช้หากต้องการมอบสิทธิ์การเริ่มกิจกรรมเบื้องหลังของตนเองให้กับบริการที่เชื่อมโยง หากต้องการเลือกใช้ แอปควรระบุ FlagBIND_ALLOW_ACTIVITY_STARTSเมื่อเรียกใช้เมธอดbindService()
การเปลี่ยนแปลงเหล่านี้จะขยายชุดข้อจำกัดที่มีอยู่เพื่อปกป้องผู้ใช้โดยการป้องกันการที่แอปที่เป็นอันตรายจะละเมิด API เพื่อเริ่มกิจกรรมที่รบกวนจากเบื้องหลัง
Zip Path Traversal
对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,Android 会通过以下方式防止 Zip 路径遍历漏洞:如果 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 ที่ไม่ใช่ SDK
Android 14 มีรายการอัปเดตของอินเทอร์เฟซที่ไม่ใช่ SDK ซึ่งถูกจำกัด โดยการทำงานร่วมกับนักพัฒนาแอป Android และการทดสอบภายในล่าสุด เราจะตรวจสอบว่ามีทางเลือกอื่นที่เผยแพร่ต่อสาธารณะพร้อมใช้งานก่อนที่จะจำกัดอินเทอร์เฟซที่ไม่ใช่ SDK ทุกครั้งที่ทำได้
หากแอปไม่ได้กำหนดเป้าหมายเป็น Android 14 การเปลี่ยนแปลงบางอย่างเหล่านี้ อาจไม่มีผลกับคุณในทันที อย่างไรก็ตาม แม้ว่าปัจจุบันคุณจะใช้ อินเทอร์เฟซที่ไม่ใช่ SDK บางรายการได้ (ขึ้นอยู่กับระดับ API เป้าหมายของแอป) แต่การใช้เมธอดหรือฟิลด์ที่ไม่ใช่ SDK ใดๆ ก็มีความเสี่ยงสูงที่จะทำให้แอป ขัดข้องเสมอ
หากต้องการดูว่าแอปใช้อินเทอร์เฟซที่ไม่ใช่ SDK อยู่หรือเปล่า คุณสามารถทดสอบ แอป ดูได้ หากแอปของคุณใช้อินเทอร์เฟซที่ไม่ใช่ SDK คุณควรเริ่มวางแผนย้ายไปใช้ทางเลือกอื่นที่เป็น SDK อย่างไรก็ตาม เราเข้าใจว่าแอปบางแอปมี Use Case ที่ถูกต้องสำหรับการใช้อินเทอร์เฟซที่ไม่ใช่ SDK หากไม่พบวิธีอื่นแทนการใช้อินเทอร์เฟซที่ไม่ใช่ SDK สำหรับฟีเจอร์ในแอป คุณควรขอ API สาธารณะใหม่
如需详细了解此 Android 版本中的变更,请参阅 Android 14 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。