Giống như các bản phát hành trước, Android 15 có các thay đổi về hành vi có thể ảnh hưởng đến ứng dụng của bạn. Những thay đổi về hành vi sau đây chỉ áp dụng cho ứng dụng nhắm đến Android 15 trở lên. Nếu ứng dụng của bạn nhắm đến Android 15 trở lên, bạn nên điều chỉnh ứng dụng để hỗ trợ những hành vi này cho phù hợp (nếu cần).
Ngoài ra, hãy nhớ tham khảo danh sách thay đổi về hành vi ảnh hưởng đến tất cả ứng dụng chạy trên Android 15 bất kể targetSdkVersion của ứng dụng.
Chức năng cốt lõi
Android 15 sửa đổi hoặc mở rộng nhiều chức năng cốt lõi của hệ thống Android.
Thay đổi đối với dịch vụ nền trước
我们将对 Android 15 中的前台服务进行以下更改。
数据同步前台服务超时行为
对于以 Android 15(API 级别 35)或更高版本为目标平台的应用,Android 15 为 dataSync 引入了新的超时行为。此行为也适用于新的 mediaProcessing 前台服务类型。
系统允许应用的 dataSync 服务在 24 小时内总共运行 6 小时,之后系统会调用正在运行的服务的 Service.onTimeout(int, int) 方法(在 Android 15 中引入)。此时,该服务有几秒钟时间来调用 Service.stopSelf()。调用 Service.onTimeout() 后,该服务将不再被视为前台服务。如果服务未调用 Service.stopSelf(),系统会抛出内部异常。系统会在 Logcat 中记录此异常,并显示以下消息:
Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"
为避免因行为变更而导致问题,您可以执行以下一项或多项操作:
- 让您的服务实现新的
Service.onTimeout(int, int)方法。当您的应用收到回调时,请务必在几秒钟内调用stopSelf()。(如果您不立即停止应用,系统会生成故障。) - 确保应用的
dataSync服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。 - 仅通过直接的用户互动来启动
dataSync前台服务;由于您的应用在服务启动时位于前台,因此服务会在应用进入后台后的 6 小时内完整运行。 - 请改用替代 API,而不是使用
dataSync前台服务。
如果您的应用的 dataSync 前台服务在过去 24 小时内运行了 6 小时,则您无法启动其他 dataSync 前台服务,除非用户已将您的应用切换到前台(这会重置计时器)。如果您尝试启动其他 dataSync 前台服务,系统会抛出 ForegroundServiceStartNotAllowedException,并显示类似“前台服务类型 dataSync 的时间限制已用尽”的错误消息。
测试
如需测试应用的行为,您可以启用数据同步超时功能,即使应用未以 Android 15 为目标平台也是如此(前提是应用在 Android 15 设备上运行)。如需启用超时,请运行以下 adb 命令:
adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name
您还可以调整超时期限,更轻松地测试应用在达到此限制时的行为。如需设置新的超时期限,请运行以下 adb 命令:
adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds
新的媒体处理前台服务类型
Android 15 giới thiệu một loại dịch vụ trên nền trước mới, mediaProcessing. Loại dịch vụ này phù hợp với các thao tác như chuyển mã tệp phương tiện. Ví dụ: một ứng dụng đa phương tiện có thể tải tệp âm thanh xuống và cần chuyển đổi tệp đó sang một định dạng khác trước khi phát. Bạn có thể sử dụng dịch vụ trên nền trước mediaProcessing để đảm bảo quá trình chuyển đổi sẽ tiếp tục ngay cả khi ứng dụng đang chạy trong nền.
Hệ thống cho phép các dịch vụ mediaProcessing của ứng dụng chạy tổng cộng 6 giờ trong khoảng thời gian 24 giờ, sau đó hệ thống sẽ gọi phương thức Service.onTimeout(int, int) của dịch vụ đang chạy (được giới thiệu trong Android 15). Tại thời điểm này, dịch vụ có vài giây để gọi Service.stopSelf(). Nếu dịch vụ không gọi Service.stopSelf(), hệ thống sẽ gửi một ngoại lệ nội bộ. Ngoại lệ được ghi lại trong Logcat với thông báo sau:
Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"
Để tránh trường hợp ngoại lệ này, bạn có thể làm theo một trong những cách sau:
- Yêu cầu dịch vụ của bạn triển khai phương thức
Service.onTimeout(int, int)mới. Khi ứng dụng của bạn nhận được lệnh gọi lại, hãy nhớ gọistopSelf()trong vòng vài giây. (Nếu bạn không dừng ứng dụng ngay lập tức, hệ thống sẽ tạo lỗi.) - Đảm bảo các dịch vụ
mediaProcessingcủa ứng dụng không chạy quá tổng cộng 6 giờ trong khoảng thời gian 24 giờ bất kỳ (trừ phi người dùng tương tác với ứng dụng, đặt lại bộ tính giờ). - Chỉ bắt đầu dịch vụ trên nền trước
mediaProcessingdo người dùng tương tác trực tiếp; vì ứng dụng của bạn đang ở nền trước khi dịch vụ bắt đầu, nên dịch vụ của bạn có đủ 6 giờ sau khi ứng dụng chuyển sang chế độ nền. - Thay vì dùng dịch vụ trên nền trước
mediaProcessing, hãy dùng một API thay thế, chẳng hạn như WorkManager.
Nếu các dịch vụ trên nền trước mediaProcessing của ứng dụng đã chạy được 6 giờ trong 24 ngày qua, thì bạn không thể bắt đầu một dịch vụ trên nền trước mediaProcessing khác trừ phi người dùng đã đưa ứng dụng của bạn lên nền trước (việc này đặt lại bộ tính giờ). Nếu bạn cố gắng bắt đầu một dịch vụ trên nền trước mediaProcessing khác, hệ thống sẽ gửi ForegroundServiceStartNotAllowedException kèm theo thông báo lỗi như "Hết thời gian giới hạn cho loại dịch vụ trên nền trước mediaProcessing".
Để biết thêm thông tin về loại dịch vụ mediaProcessing, hãy xem phần Thay đổi đối với loại dịch vụ trên nền trước cho Android 15: Xử lý nội dung nghe nhìn.
Thử nghiệm
Để kiểm thử hành vi của ứng dụng, bạn có thể bật thời gian chờ xử lý nội dung nghe nhìn ngay cả khi ứng dụng của bạn không nhắm đến Android 15 (miễn là ứng dụng đang chạy trên thiết bị Android 15). Để bật tính năng thời gian chờ, hãy chạy lệnh adb sau:
adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name
Bạn cũng có thể điều chỉnh khoảng thời gian chờ để dễ dàng kiểm thử cách ứng dụng của bạn hoạt động khi đạt đến giới hạn. Để đặt khoảng thời gian chờ mới, hãy chạy lệnh adb sau:
adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds
对启动前台服务的 BOOT_COMPLETED 广播接收器的限制
在启动 BOOT_COMPLETED 广播接收器方面存在新限制
前台服务。BOOT_COMPLETED 接收器不能启动
以下类型的前台服务:
dataSynccameramediaPlaybackphoneCallmediaProjectionmicrophone(自 Android 14 起,microphone就受到此限制)
如果 BOOT_COMPLETED 接收器尝试启动任何上述类型的前台
服务,系统会抛出 ForegroundServiceStartNotAllowedException。
测试
如需测试应用的行为,您可以启用这些新限制,即使您的应用并未以 Android 15 为目标平台(只要应用在 Android 15 设备上运行)也是如此。运行以下 adb 命令:
adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name
如需在不重启设备的情况下发送 BOOT_COMPLETED 广播,请运行以下 adb 命令:
adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name
在应用拥有 SYSTEM_ALERT_WINDOW 权限时启动前台服务的限制
Trước đây, nếu có quyền SYSTEM_ALERT_WINDOW, ứng dụng có thể chạy một dịch vụ trên nền trước ngay cả khi ứng dụng đó đang chạy ở chế độ nền (như đã thảo luận trong phần các trường hợp miễn trừ khỏi các quy định hạn chế về việc bắt đầu ở chế độ nền).
Nếu một ứng dụng nhắm đến Android 15, thì trường hợp miễn trừ này hiện sẽ hẹp hơn. Ứng dụng hiện cần có quyền SYSTEM_ALERT_WINDOW và cũng có một cửa sổ lớp phủ hiển thị. Tức là trước tiên, ứng dụng cần khởi chạy cửa sổ TYPE_APPLICATION_OVERLAY và cửa sổ đó cần hiển thị trước khi bạn bắt đầu dịch vụ trên nền trước.
Nếu ứng dụng của bạn cố gắng bắt đầu một dịch vụ trên nền trước từ chế độ nền mà không đáp ứng các yêu cầu mới này (và không có một số trường hợp ngoại lệ khác), thì hệ thống sẽ gửi ForegroundServiceStartNotAllowedException.
Nếu ứng dụng của bạn khai báo quyền SYSTEM_ALERT_WINDOW và chạy các dịch vụ trên nền trước từ chế độ nền, thì ứng dụng đó có thể bị ảnh hưởng bởi thay đổi này. Nếu ứng dụng của bạn nhận được ForegroundServiceStartNotAllowedException, hãy kiểm tra thứ tự hoạt động của ứng dụng và đảm bảo ứng dụng đã có cửa sổ lớp phủ đang hoạt động trước khi ứng dụng đó cố gắng bắt đầu một dịch vụ trên nền trước từ chế độ nền. Bạn có thể kiểm tra xem cửa sổ lớp phủ của mình hiện có hiển thị hay không bằng cách gọi View.getWindowVisibility() hoặc bạn có thể ghi đè View.onWindowVisibilityChanged() để nhận thông báo bất cứ khi nào chế độ hiển thị thay đổi.
Thử nghiệm
Để kiểm thử hành vi của ứng dụng, bạn có thể bật các quy định hạn chế mới này ngay cả khi ứng dụng của bạn không nhắm đến Android 15 (miễn là ứng dụng đang chạy trên thiết bị Android 15). Để bật các hạn chế mới này khi khởi động dịch vụ trên nền trước từ chế độ nền, hãy chạy lệnh adb sau:
adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name
Thay đổi về thời điểm ứng dụng có thể sửa đổi trạng thái chung của chế độ Không làm phiền
以 Android 15(API 级别 35)及更高版本为目标平台的应用无法再更改设备上的勿扰 (DND) 功能的全局状态或政策(无论是通过修改用户设置还是关闭勿扰模式)。相反,应用必须提供 AutomaticZenRule,系统会将其与现有的“最严格的政策优先”方案合并为一个全局政策。对之前会影响全局状态的现有 API 的调用(setInterruptionFilter、setNotificationPolicy)会导致创建或更新隐式 AutomaticZenRule,该 AutomaticZenRule 会根据这些 API 调用的调用周期开启和关闭。
请注意,只有当应用调用 setInterruptionFilter(INTERRUPTION_FILTER_ALL) 并希望该调用停用之前由其所有者激活的 AutomaticZenRule 时,此更改才会影响可观察到的行为。
Thay đổi về API OpenJDK
Android 15 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致。
其中一些变更可能会影响以 Android 15(API 级别 35)为目标平台的应用兼容性:
对字符串格式化 API 进行了更改:现在,在使用以下
String.format()和Formatter.format()API 时,对参数索引、标志、宽度和精度的验证要求变得更加严格:String.format(String, Object[])String.format(Locale, String, Object[])Formatter.format(String, Object[])Formatter.format(Locale, String, Object[])
例如,如果使用参数索引 0(格式字符串中的
%0),则会抛出以下异常:IllegalFormatArgumentIndexException: Illegal format argument index = 0在这种情况下,可以通过使用实参索引 1(格式字符串中的
%1)来解决此问题。Arrays.asList(...).toArray()的组件类型发生变化:使用Arrays.asList(...).toArray()时,生成的数组的组件类型现在是Object,而不是底层数组元素的类型。因此,以下代码会抛出ClassCastException:String[] elements = (String[]) Arrays.asList("one", "two").toArray();在这种情况下,为了在生成的数组中保留
String作为组件类型,您可以改用Collection.toArray(Object[]):String[] elements = Arrays.asList("two", "one").toArray(new String[0]);语言代码处理方式变更:使用
LocaleAPI 时,希伯来语、意第绪语和印度尼西亚语的语言代码不再转换为其过时形式(希伯来语:iw,意第绪语:ji,印度尼西亚语:in)。为这些语言区域之一指定语言代码时,请改用 ISO 639-1 中的代码(希伯来语:he,意第绪语:yi,印度尼西亚语:id)。对随机 int 序列的更改:在 https://bugs.openjdk.org/browse/JDK-8301574 中做出更改后,以下
Random.ints()方法现在返回的数字序列与Random.nextInt()方法不同:通常,此更改不会导致应用行为异常,但您的代码不应期望从
Random.ints()方法生成的序列与Random.nextInt()匹配。
当您在应用的 build 配置中将 compileSdk 更新为使用 Android 15(API 级别 35)后,新的 SequencedCollection API 可能会影响应用的兼容性:
与
kotlin-stdlib中的MutableList.removeFirst()和MutableList.removeLast()扩展函数冲突Java 中的
List类型会映射到 Kotlin 中的MutableList类型。 由于List.removeFirst()和List.removeLast()API 已在 Android 15(API 级别 35)中引入,因此 Kotlin 编译器会将函数调用(例如list.removeFirst())静态解析为新的ListAPI,而不是kotlin-stdlib中的扩展函数。如果应用在重新编译时将
compileSdk设为35,将minSdk设为34或更低,然后在 Android 14 及更低版本上运行,则会抛出运行时错误:java.lang.NoSuchMethodError: No virtual method removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;Android Gradle 插件中的现有
NewApilint 选项可以捕获这些新的 API 用法。./gradlew lintMainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi] list.removeFirst()如需修复运行时异常和 lint 错误,可以在 Kotlin 中分别将
removeFirst()和removeLast()函数调用替换为removeAt(0)和removeAt(list.lastIndex)。如果您使用的是 Android Studio Ladybug | 2024.1.3 或更高版本,它还提供了针对这些错误的快速修复选项。如果 lint 选项已被停用,请考虑移除
@SuppressLint("NewApi")和lintOptions { disable 'NewApi' }。与 Java 中的其他方法冲突
现有类型中添加了新方法,例如
List和Deque。这些新方法可能与其他接口和类中具有相同名称和参数类型的方法不兼容。如果方法签名冲突且不兼容,javac编译器会输出构建时错误。例如:错误示例 1:
javac MyList.javaMyList.java:135: error: removeLast() in MyList cannot implement removeLast() in List public void removeLast() { ^ return type void is not compatible with Object where E is a type-variable: E extends Object declared in interface List错误示例 2:
javac MyList.javaMyList.java:7: error: types Deque<Object> and List<Object> are incompatible; public class MyList implements List<Object>, Deque<Object> { both define reversed(), but with unrelated return types 1 error错误示例 3:
javac MyList.javaMyList.java:43: error: types List<E#1> and MyInterface<E#2> are incompatible; public static class MyList implements List<Object>, MyInterface<Object> { class MyList inherits unrelated defaults for getFirst() from types List and MyInterface where E#1,E#2 are type-variables: E#1 extends Object declared in interface List E#2 extends Object declared in interface MyInterface 1 error如需修复这些构建错误,实现这些接口的类应使用兼容的返回值类型替换该方法。例如:
@Override public Object getFirst() { return List.super.getFirst(); }
Bảo mật
Android 15 có những thay đổi giúp tăng cường bảo mật hệ thống để bảo vệ ứng dụng và người dùng khỏi các ứng dụng độc hại.
Các phiên bản TLS bị hạn chế
Android 15 限制了对 TLS 版本 1.0 和 1.1 的使用。这些版本之前已在 Android 中被弃用,但现在不允许面向 Android 15 的应用使用。
Khởi chạy hoạt động ở chế độ nền một cách an toàn
Android 15 做出了一些变更,可防止恶意后台应用将其他应用置于前台、提升自身权限并滥用用户互动,从而保护用户免受恶意应用的侵害,并让用户更好地控制自己的设备。自 Android 10(API 级别 29)起,后台 activity 启动受到限制。
其他更改
- 将
PendingIntent创建者更改为默认阻止后台活动启动。这有助于防止应用意外创建可能被恶意行为者滥用的PendingIntent。 - 除非
PendingIntent发送方允许,否则请勿将应用转至前台。此变更旨在防止恶意应用滥用在后台启动 activity 的功能。默认情况下,除非创建者允许后台 activity 启动权限或发送者具有后台 activity 启动权限,否则不允许应用将任务堆栈带到前台。 - 控制任务堆栈的顶层 activity 如何完成其任务。如果顶部 activity 完成了一项任务,Android 将返回到上次处于活跃状态的任务。此外,如果非顶部 activity 完成其任务,Android 会返回到主屏幕;它不会阻止此非顶部 activity 完成。
- 防止从其他应用启动任意 activity 进入您自己的任务。此变更可防止恶意应用通过创建看似来自其他应用的 activity 来对用户进行钓鱼式攻击。
- 阻止将非可见窗口纳入后台 activity 启动的考虑范围。这有助于防止恶意应用滥用后台活动启动来向用户显示不必要或恶意的内容。
Ý định an toàn hơn
Android 15 giới thiệu StrictMode cho các ý định.
Để xem nhật ký chi tiết về các lỗi vi phạm khi sử dụng Intent, hãy sử dụng phương thức sau:
Kotlin
fun onCreate() { StrictMode.setVmPolicy(VmPolicy.Builder() .detectUnsafeIntentLaunch() .build() ) }
Java
public void onCreate() { StrictMode.setVmPolicy(new VmPolicy.Builder() .detectUnsafeIntentLaunch() .build()); }
Trải nghiệm người dùng và giao diện người dùng hệ thống
Android 15 có một số thay đổi nhằm tạo ra trải nghiệm người dùng nhất quán và trực quan hơn.
Thay đổi lồng ghép cửa sổ
Có hai thay đổi liên quan đến phần lồng ghép cửa sổ trong Android 15: chế độ tràn viền được thực thi theo mặc định và cũng có các thay đổi về cấu hình, chẳng hạn như cấu hình mặc định của các thanh hệ thống.
全面实施政策
Theo mặc định, ứng dụng sẽ hiển thị tràn viền trên các thiết bị chạy Android 15 nếu ứng dụng đó nhắm đến Android 15 (cấp độ API 35).
Đây là một thay đổi có thể gây lỗi và ảnh hưởng tiêu cực đến giao diện người dùng của ứng dụng. Những thay đổi này ảnh hưởng đến các khu vực giao diện người dùng sau:
- Thanh điều hướng bằng cử chỉ
- Trong suốt theo mặc định.
- Phần bù trừ dưới cùng bị tắt nên nội dung sẽ nằm phía sau thanh điều hướng của hệ thống, trừ phi áp dụng phần lồng ghép.
setNavigationBarColorvàR.attr#navigationBarColorkhông còn được dùng nữa và không ảnh hưởng đến điều hướng bằng cử chỉ.setNavigationBarContrastEnforcedvàR.attr#navigationBarContrastEnforcedtiếp tục không ảnh hưởng đến điều hướng bằng cử chỉ.
- Thao tác bằng 3 nút
- Độ mờ được đặt thành 80% theo mặc định, với màu có thể khớp với nền cửa sổ.
- Đã tắt độ lệch dưới cùng để nội dung nằm phía sau thanh điều hướng hệ thống, trừ phi áp dụng phần lồng ghép.
setNavigationBarColorvàR.attr#navigationBarColorđược đặt để khớp với nền cửa sổ theo mặc định. Nền cửa sổ phải là một đối tượng có thể vẽ màu để áp dụng giá trị mặc định này. API này không còn được dùng nữa nhưng vẫn ảnh hưởng đến chế độ thao tác bằng 3 nút.setNavigationBarContrastEnforcedvàR.attr#navigationBarContrastEnforcedlà true theo mặc định, giúp thêm một nền mờ 80% trên chế độ thao tác bằng 3 nút.
- Thanh trạng thái
- Trong suốt theo mặc định.
- Phần bù trừ trên cùng bị tắt nên nội dung sẽ nằm phía sau thanh trạng thái, trừ phi áp dụng phần lồng ghép.
setStatusBarColorvàR.attr#statusBarColorkhông còn được dùng nữa và không có hiệu lực trên Android 15.setStatusBarContrastEnforcedvàR.attr#statusBarContrastEnforcedkhông còn được dùng nữa nhưng vẫn có hiệu lực trên Android 15.
- Vết cắt trên màn hình
layoutInDisplayCutoutModecủa cửa sổ không nổi phải làLAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS.SHORT_EDGES,NEVERvàDEFAULTđược diễn giải làALWAYSđể người dùng không thấy thanh màu đen do vết cắt trên màn hình gây ra và xuất hiện tràn viền.
Ví dụ sau đây cho thấy một ứng dụng trước và sau khi nhắm đến Android 15 (cấp độ API 35), cũng như trước và sau khi áp dụng các phần lồng ghép. Ví dụ này không đầy đủ, có thể xuất hiện khác trên Android Auto.
Những điều cần kiểm tra nếu ứng dụng của bạn đã tràn viền
Nếu ứng dụng của bạn đã tràn viền và áp dụng các phần lồng ghép, thì bạn hầu như không bị ảnh hưởng, ngoại trừ trong các trường hợp sau. Tuy nhiên, ngay cả khi bạn cho rằng mình không bị ảnh hưởng, bạn vẫn nên kiểm thử ứng dụng.
- Bạn có một cửa sổ không nổi, chẳng hạn như
Activitysử dụngSHORT_EDGES,NEVERhoặcDEFAULTthay vìLAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Nếu ứng dụng của bạn gặp sự cố khi khởi chạy, thì có thể là do màn hình chờ. Bạn có thể nâng cấp phần phụ thuộc core splashscreen lên phiên bản 1.2.0-alpha01 trở lên hoặc đặtwindow.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always. - Có thể có những màn hình có lưu lượng truy cập thấp hơn với giao diện người dùng bị che khuất. Xác minh rằng những màn hình ít được truy cập này không có giao diện người dùng bị che khuất. Các màn hình có lưu lượng truy cập thấp hơn bao gồm:
- Màn hình giới thiệu hoặc màn hình đăng nhập
- Trang cài đặt
Những điều cần kiểm tra nếu ứng dụng của bạn chưa tràn viền
Nếu ứng dụng của bạn chưa tràn viền, thì rất có thể ứng dụng đó sẽ bị ảnh hưởng. Ngoài các trường hợp dành cho ứng dụng đã tràn viền, bạn nên cân nhắc những điều sau:
- Nếu ứng dụng của bạn dùng các thành phần Material 3 (
androidx.compose.material3) trong Compose, chẳng hạn nhưTopAppBar,BottomAppBarvàNavigationBar, thì các thành phần này có thể không bị ảnh hưởng vì chúng tự động xử lý phần lồng ghép. - Nếu ứng dụng của bạn dùng các Thành phần Material 2 (
androidx.compose.material) trong Compose, thì các thành phần này sẽ không tự động xử lý phần lồng ghép. Tuy nhiên, bạn có thể truy cập vào những phần lồng ghép này và áp dụng chúng theo cách thủ công. Trong androidx.compose.material 1.6.0 trở lên, hãy dùng tham sốwindowInsetsđể áp dụng phần lồng ghép theo cách thủ công choBottomAppBar,TopAppBar,BottomNavigationvàNavigationRail. Tương tự, hãy dùng tham sốcontentWindowInsetschoScaffold. - Nếu ứng dụng của bạn dùng các thành phần Khung hiển thị và Material (
com.google.android.material), thì hầu hết thành phần Material dựa trên Khung hiển thị (chẳng hạn nhưBottomNavigationView,BottomAppBar,NavigationRailViewhoặcNavigationView) sẽ xử lý phần lồng ghép nên bạn không cần phải làm gì thêm. Tuy nhiên, bạn cần phải thêmandroid:fitsSystemWindows="true"nếu dùngAppBarLayout. - Đối với các thành phần kết hợp tuỳ chỉnh, hãy áp dụng phần lồng ghép theo cách thủ công như khoảng đệm. Nếu nội dung của bạn nằm trong
Scaffold, bạn có thể sử dụng các phần lồng ghép bằng cách dùng giá trị khoảng đệmScaffold. Nếu không, hãy áp dụng khoảng đệm bằng một trong cácWindowInsets. - Nếu ứng dụng của bạn dùng khung hiển thị
BottomSheet,SideSheethoặc các vùng chứa tuỳ chỉnh, hãy áp dụng khoảng đệm bằngViewCompat.setOnApplyWindowInsetsListener. Đối vớiRecyclerView, hãy áp dụng khoảng đệm bằng trình nghe này, đồng thời thêmclipToPadding="false".
Những điều cần kiểm tra nếu ứng dụng của bạn phải cung cấp biện pháp bảo vệ tuỳ chỉnh ở chế độ nền
Nếu ứng dụng của bạn phải cung cấp tính năng bảo vệ nền tuỳ chỉnh cho thao tác bằng 3 nút hoặc thanh trạng thái, thì ứng dụng đó phải đặt một composable hoặc khung hiển thị phía sau thanh hệ thống bằng cách sử dụng WindowInsets.Type#tappableElement() để lấy chiều cao của thanh điều hướng 3 nút hoặc WindowInsets.Type#statusBars.
Tài nguyên bổ sung về chế độ tràn viền
Hãy xem hướng dẫn về Chế độ xem tràn viền và Thành phần kết hợp tràn viền để biết thêm các điểm cần cân nhắc khi áp dụng phần lồng ghép.
API không dùng nữa
Các API sau đây không còn được dùng nữa nhưng chưa bị vô hiệu hoá:
R.attr#enforceStatusBarContrastR.attr#navigationBarColor(đối với thao tác bằng 3 nút, có độ trong suốt 80%)Window#isStatusBarContrastEnforcedWindow#setNavigationBarColor(đối với thao tác bằng 3 nút, có độ mờ 80%)Window#setStatusBarContrastEnforced
Các API sau đây không còn được dùng nữa và đã bị vô hiệu hoá:
R.attr#navigationBarColor(để điều hướng bằng cử chỉ)R.attr#navigationBarDividerColorR.attr#statusBarColorWindow#setDecorFitsSystemWindowsWindow#getNavigationBarColorWindow#getNavigationBarDividerColorWindow#getStatusBarColorWindow#setNavigationBarColor(để điều hướng bằng cử chỉ)Window#setNavigationBarDividerColorWindow#setStatusBarColor
稳定配置
如果您的应用以 Android 15(API 级别 35)或更高版本为目标平台,Configuration 将不再排除系统栏。如果您在 Configuration 类中使用屏幕尺寸进行布局计算,则应根据需要将其替换为更好的替代方案,例如适当的 ViewGroup、WindowInsets 或 WindowMetricsCalculator。
Configuration 从 API 1 开始提供。通常从 Activity.onConfigurationChanged 获取。它提供窗口密度、方向和大小等信息。从 Configuration 返回的窗口大小有一个重要特征,即它之前不包含系统栏。
配置大小通常用于资源选择(例如 /res/layout-h500dp),这仍然是一个有效的用例。不过,我们一直不建议将其用于布局计算。如果您这样做,现在应该停止。您应根据自己的应用场景,将 Configuration 替换为更合适的内容。
如果您使用它来计算布局,请使用适当的 ViewGroup,例如 CoordinatorLayout 或 ConstraintLayout。如果您使用它来确定系统导航栏的高度,请使用 WindowInsets。如果您想知道应用窗口的当前大小,请使用 computeCurrentWindowMetrics。
以下列表介绍了受此变更影响的字段:
Configuration.screenWidthDp和screenHeightDp尺寸不再排除系统栏。Configuration.smallestScreenWidthDp会间接受到screenWidthDp和screenHeightDp更改的影响。Configuration.orientation会间接受到接近方形的设备上screenWidthDp和screenHeightDp更改的影响。Display.getSize(Point)间接受到Configuration中更改的影响。此方法从 API 级别 30 开始已废弃。- 自 API 级别 33 起,
Display.getMetrics()就已经采用这种工作方式。
thuộc tính elegantTextHeight mặc định là true
对于以 Android 15(API 级别 35)为目标平台的应用,elegantTextHeight TextView 属性默认会变为 true,将默认使用的紧凑字体替换为一些具有较大垂直测量的脚本,使其更易于阅读。紧凑字体旨在防止布局中断;Android 13(API 级别 33)允许文本布局利用 fallbackLineSpacing 属性拉伸垂直高度,从而防止许多此类中断。
在 Android 15 中,系统中仍保留了紧凑字体,因此您的应用可以将 elegantTextHeight 设置为 false 以获得与之前相同的行为,但即将发布的版本不太可能支持此字体。因此,如果您的应用支持以下脚本:阿拉伯语、老挝语、缅甸语、泰米尔语、古吉拉特语、卡纳达语、马拉雅拉姆语、奥里亚语、泰卢固语或泰语,请将 elegantTextHeight 设置为 true 以测试您的应用。
elegantTextHeight 行为。
elegantTextHeight 行为。Chiều rộng TextView thay đổi đối với các hình dạng chữ cái phức tạp
在以前的 Android 版本中,某些具有复杂形状的手写字体或语言可能会在上一个或下一个字符的区域绘制字母。在某些情况下,此类字母会在开头或结尾处被剪裁。从 Android 15 开始,TextView 会分配宽度,以便为此类字母绘制足够的空间,并允许应用请求向左额外添加内边距以防止剪裁。
由于此更改会影响 TextView 确定宽度的方式,因此如果应用以 Android 15(API 级别 35)或更高版本为目标平台,TextView 会默认分配更多宽度。您可以通过对 TextView 调用 setUseBoundsForWidth API 来启用或停用此行为。
由于添加左内边距可能会导致现有布局未对齐,因此默认情况下不会添加内边距,即使以 Android 15 或更高版本为目标平台的应用也是如此。不过,您可以通过调用 setShiftDrawingOffsetForStartOverhang 添加额外的内边距以防止剪裁。
以下示例展示了这些更改如何改进某些字体和语言的文本布局。
<TextView android:fontFamily="cursive" android:text="java" />
<TextView android:fontFamily="cursive" android:text="java" android:useBoundsForWidth="true" android:shiftDrawingOffsetForStartOverhang="true" />
<TextView android:text="คอมพิวเตอร์" />
<TextView android:text="คอมพิวเตอร์" android:useBoundsForWidth="true" android:shiftDrawingOffsetForStartOverhang="true" />
Chiều cao dòng mặc định theo ngôn ngữ cho EditText
在较低版本的 Android 中,文本布局会拉伸文本的高度,以满足与当前语言区域匹配的字体的行高。例如,如果内容是日语,由于日语字体的行高略高于拉丁字体,因此文本的高度会略高。不过,尽管行高存在这些差异,但无论使用的是哪种语言区域,EditText 元素的大小都是统一的,如下图所示:
EditText 元素。EditText 的高度相同,即使这些语言的行高各不相同。对于以 Android 15(API 级别 35)为目标平台的应用,现在为 EditText 预留了最小行高,以匹配指定语言区域的参考字体,如下图所示:
EditText 元素。EditText 的高度现在包含足够的空间来容纳这些语言字体的默认行高。如有需要,您的应用可以将 useLocalePreferredLineHeightForMinimum 属性指定为 false,以恢复之前的行为;您的应用还可以在 Kotlin 和 Java 中使用 setMinimumFontMetrics API 设置自定义最小垂直指标。
Camera và nội dung nghe nhìn
Android 15 có những thay đổi sau đây đối với hành vi của camera và nội dung nghe nhìn cho các ứng dụng nhắm đến Android 15 trở lên.
Các quy định hạn chế về việc yêu cầu quyền phát âm thanh
以 Android 15(API 级别 35)为目标平台的应用必须是顶部应用或正在运行前台服务,才能请求音频焦点。如果应用在未满足上述任一要求的情况下尝试请求焦点,调用将返回 AUDIOFOCUS_REQUEST_FAILED。
如需详细了解音频焦点,请参阅管理音频焦点。
Các quy tắc hạn chế mới cập nhật đối với yếu tố ngoài SDK
Android 15 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。
如果您的应用并非以 Android 15 为目标平台,其中一些变更可能不会立即对您产生影响。不过,虽然您的应用可以访问某些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,就很有可能会造成应用功能异常。
如果您不确定自己的应用是否使用了非 SDK 接口,则可以通过测试该应用来进行确认。如果您的应用依赖非 SDK 接口,则应开始计划迁移到 SDK 替代方案。不过,我们知道某些应用存在使用非 SDK 接口的合理场景。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,则应该请求新的公共 API。
如需详细了解此 Android 版本中的变更,请参阅 Android 15 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。