Thay đổi về hành vi: Ứng dụng nhắm đến Android 15 trở lên

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 các 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ụ trên nền trước

Chúng tôi sẽ thực hiện những thay đổi sau đây đối với các dịch vụ trên nền trước trong Android 15.

Hành vi hết thời gian chờ của dịch vụ đồng bộ hoá dữ liệu trên nền trước

Android 15 giới thiệu một hành vi hết thời gian chờ mới cho dataSync đối với các ứng dụng nhắm đến Android 15 (API cấp 35) trở lên. Hành vi này cũng áp dụng cho loại dịch vụ trên nền trước mediaProcessing mới.

Hệ thống cho phép các dịch vụ dataSync 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(). Khi Service.onTimeout() được gọi, dịch vụ này không còn được coi là dịch vụ trên nền trước. 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 dataSync did not stop within its timeout: [component name]"

Để tránh các vấn đề liên quan đến thay đổi về hành vi này, bạn có thể làm một hoặc nhiều việc sau:

  1. Yêu cầu dịch vụ 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ọi stopSelf() trong 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.)
  2. Đảm bảo các dịch vụ dataSync củ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ờ).
  3. Chỉ bắt đầu dịch vụ trên nền trước dataSync do 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.
  4. Thay vì sử dụng dịch vụ trên nền trước dataSync, hãy sử dụng API thay thế.

Nếu các dịch vụ trên nền trước dataSync của ứng dụng đã chạy trong 6 giờ trong 24 giờ qua, thì bạn không thể bắt đầu một dịch vụ trên nền trước dataSync khác trừ phi người dùng đã đưa ứng dụng của bạn lên nền trước (điều này sẽ đặt lại bộ hẹn giờ). Nếu bạn cố gắng khởi động một dịch vụ trên nền trước dataSync 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 dataSync".

Thử nghiệm

Để kiểm thử hành vi của ứng dụng, bạn có thể bật thời gian chờ đồng bộ hoá dữ liệu ngay cả khi ứng dụng 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 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 data_sync_fgs_timeout_duration duration-in-milliseconds

Loại dịch vụ trên nền trước mới để xử lý nội dung nghe nhìn

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:

  1. 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ọi stopSelf() 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.)
  2. Đảm bảo các dịch vụ mediaProcessing củ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ờ).
  3. Chỉ bắt đầu dịch vụ trên nền trước mediaProcessing do 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.
  4. 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

Các hạn chế đối với bộ nhận thông báo truyền tin BOOT_COMPLETED khởi chạy dịch vụ trên nền trước

在启动 BOOT_COMPLETED 广播接收器方面存在新限制 前台服务。BOOT_COMPLETED 接收器能启动 以下类型的前台服务:

如果 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

Các hạn chế về việc khởi động dịch vụ trên nền trước khi ứng dụng có quyền SYSTEM_ALERT_WINDOW

以前,如果应用拥有 SYSTEM_ALERT_WINDOW 权限,即使应用当前在后台运行,也可以启动前台服务(如免于后台启动限制中所述)。

如果应用以 Android 15 为目标平台,则此豁免范围现在更窄。现在,应用需要具有 SYSTEM_ALERT_WINDOW 权限,并且需要有一个可见的叠加窗口。也就是说,应用需要先启动 TYPE_APPLICATION_OVERLAY 窗口,并且该窗口需要处于可见状态,然后您才能启动前台服务。

如果您的应用尝试从后台启动前台服务,但不符合这些新要求(并且没有其他豁免情况),系统会抛出 ForegroundServiceStartNotAllowedException

如果您的应用声明了 SYSTEM_ALERT_WINDOW 权限并从后台启动前台服务,则可能会受到此变更的影响。如果您的应用获得了 ForegroundServiceStartNotAllowedException,请检查应用的操作顺序,并确保应用在尝试从后台启动前台服务之前已具有有效的叠加层窗口。您可以通过调用 View.getWindowVisibility() 检查叠加层窗口当前是否可见,也可以替换 View.onWindowVisibilityChanged(),以便在可见性发生变化时收到通知。

测试

如需测试应用的行为,您可以启用这些新限制,即使您的应用并未以 Android 15 为目标平台(只要应用在 Android 15 设备上运行)也是如此。如需针对从后台启动前台服务启用这些新限制,请运行以下 adb 命令:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

Những thay đổi về thời điểm các ứng dụng có thể sửa đổi trạng thái chung của chế độ Không làm phiền

Các ứng dụng nhắm đến Android 15 (API cấp 35) trở lên không thể thay đổi trạng thái hoặc chính sách chung của chế độ Không làm phiền (DND) trên thiết bị nữa (bằng cách sửa đổi chế độ cài đặt của người dùng hoặc tắt chế độ DND). Thay vào đó, ứng dụng phải đóng góp một AutomaticZenRule. Hệ thống sẽ kết hợp chính sách này vào một chính sách chung với lược đồ hiện có là chính sách hạn chế nhất sẽ thắng. Các lệnh gọi đến các API hiện có từng ảnh hưởng đến trạng thái toàn cục (setInterruptionFilter, setNotificationPolicy) sẽ dẫn đến việc tạo hoặc cập nhật một AutomaticZenRule ngầm ẩn. Lệnh gọi này được bật và tắt tuỳ thuộc vào chu kỳ gọi của các lệnh gọi API đó.

Xin lưu ý rằng thay đổi này chỉ ảnh hưởng đến hành vi có thể quan sát được nếu ứng dụng đang gọi setInterruptionFilter(INTERRUPTION_FILTER_ALL) và dự kiến lệnh gọi đó sẽ huỷ kích hoạt AutomaticZenRule mà chủ sở hữu của ứng dụng đã kích hoạt trước đó.

Các thay đổi về API của OpenJDK

Android 15 tiếp tục công cuộc làm mới các thư viện cốt lõi của Android để phù hợp với các tính năng trong bản phát hành LTS OpenJDK mới nhất.

Một số thay đổi sau đây có thể ảnh hưởng đến khả năng tương thích của ứng dụng đối với những ứng dụng nhắm đến Android 15 (API cấp 35):

  • Thay đổi đối với các API định dạng chuỗi: Giờ đây, việc xác thực chỉ mục đối số, cờ, chiều rộng và độ chính xác sẽ nghiêm ngặt hơn khi bạn dùng các API String.format()Formatter.format() sau đây:

    Ví dụ: ngoại lệ sau sẽ được gửi khi chỉ mục đối số là 0 (%0 trong chuỗi định dạng):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    Trong trường hợp này, bạn có thể khắc phục vấn đề bằng cách sử dụng chỉ mục đối số là 1 (%1 trong chuỗi định dạng).

  • Thay đổi đối với loại thành phần của Arrays.asList(...).toArray(): Khi sử dụng Arrays.asList(...).toArray(), loại thành phần của mảng kết quả hiện là Object, chứ không phải loại của các phần tử trong mảng cơ bản. Vì vậy, mã sau đây sẽ gửi một ClassCastException:

    String[] elements = (String[]) Arrays.asList("one", "two").toArray();
    

    Trong trường hợp này, để giữ String làm loại thành phần trong mảng kết quả, bạn có thể sử dụng Collection.toArray(Object[]):

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Các thay đổi đối với việc xử lý mã ngôn ngữ: Khi sử dụng API Locale, mã ngôn ngữ cho tiếng Do Thái, tiếng Yiddish và tiếng Indonesia sẽ không còn được chuyển đổi sang các dạng thức cũ (tiếng Do Thái: iw, tiếng Yiddish: ji và tiếng Indonesia: in). Khi chỉ định mã ngôn ngữ cho một trong các ngôn ngữ này, hãy sử dụng mã theo tiêu chuẩn ISO 639-1 (tiếng Do Thái: he, tiếng Yiddish: yi và tiếng Indonesia: id).

  • Các thay đổi đối với chuỗi số nguyên ngẫu nhiên: Sau các thay đổi được thực hiện trong https://bugs.openjdk.org/browse/JDK-8301574, các phương thức Random.ints() sau đây hiện trả về một chuỗi số khác với các phương thức Random.nextInt():

    Nhìn chung, thay đổi này sẽ không dẫn đến hành vi làm hỏng ứng dụng, nhưng mã của bạn không được mong đợi trình tự được tạo từ các phương thức Random.ints() khớp với Random.nextInt().

API SequencedCollection mới có thể ảnh hưởng đến khả năng tương thích của ứng dụng sau khi bạn cập nhật compileSdk trong cấu hình bản dựng của ứng dụng để sử dụng Android 15 (API cấp 35):

  • Xung đột với các hàm mở rộng MutableList.removeFirst()MutableList.removeLast() trong kotlin-stdlib

    Loại List trong Java được ánh xạ đến loại MutableList trong Kotlin. Vì các API List.removeFirst()List.removeLast() đã được ra mắt trong Android 15 (API cấp 35), nên trình biên dịch Kotlin sẽ giải quyết các lệnh gọi hàm (ví dụ: list.removeFirst()) một cách tĩnh đến các API List mới thay vì đến các hàm mở rộng trong kotlin-stdlib.

    Nếu một ứng dụng được biên dịch lại với compileSdk được đặt thành 35minSdk được đặt thành 34 trở xuống, rồi ứng dụng đó chạy trên Android 14 trở xuống, thì sẽ xảy ra lỗi thời gian chạy:

    java.lang.NoSuchMethodError: No virtual method
    removeFirst()Ljava/lang/Object; in class Ljava/util/ArrayList;
    

    Lựa chọn NewApi lint hiện có trong Trình bổ trợ Android cho Gradle có thể phát hiện những cách sử dụng API mới này.

    ./gradlew lint
    
    MainActivity.kt:41: Error: Call requires API level 35 (current min is 34): java.util.List#removeFirst [NewApi]
          list.removeFirst()
    

    Để khắc phục ngoại lệ thời gian chạy và lỗi lint, bạn có thể thay thế các lệnh gọi hàm removeFirst()removeLast() bằng removeAt(0)removeAt(list.lastIndex) tương ứng trong Kotlin. Nếu bạn đang sử dụng Android Studio Ladybug | 2024.1.3 trở lên, thì phiên bản này cũng cung cấp lựa chọn sửa nhanh cho những lỗi này.

    Hãy cân nhắc việc xoá @SuppressLint("NewApi")lintOptions { disable 'NewApi' } nếu bạn đã tắt lựa chọn lint.

  • Xung đột với các phương thức khác trong Java

    Các phương thức mới đã được thêm vào các loại hiện có, ví dụ: ListDeque. Những phương thức mới này có thể không tương thích với các phương thức có cùng tên và kiểu đối số trong các giao diện và lớp khác. Trong trường hợp xảy ra xung đột chữ ký phương thức với sự không tương thích, trình biên dịch javac sẽ xuất ra lỗi thời gian tạo. Ví dụ:

    Ví dụ về lỗi 1:

    javac MyList.java
    
    MyList.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
    

    Ví dụ về lỗi 2:

    javac MyList.java
    
    MyList.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
    

    Ví dụ về lỗi 3:

    javac MyList.java
    
    MyList.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
    

    Để khắc phục những lỗi này khi tạo bản dựng, lớp triển khai các giao diện này phải ghi đè phương thức bằng một kiểu dữ liệu trả về tương thích. Ví dụ:

    @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 tính bảo mật của 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 hạn chế việc sử dụng TLS phiên bản 1.0 và 1.1. Các phiên bản này trước đây đã ngừng hoạt động trong Android, nhưng hiện không được phép sử dụng cho các ứng dụng nhắm đến Android 15.

Khởi chạy hoạt động trong nền một cách an toàn

Android 15 protects users from malicious apps and gives them more control over their devices by adding changes that prevent malicious background apps from bringing other apps to the foreground, elevating their privileges, and abusing user interaction. Background activity launches have been restricted since Android 10 (API level 29).

Other changes

In addition to the restriction for UID matching, these other changes are also included:

  • Change PendingIntent creators to block background activity launches by default. This helps prevent apps from accidentally creating a PendingIntent that could be abused by malicious actors.
  • Don't bring an app to the foreground unless the PendingIntent sender allows it. This change aims to prevent malicious apps from abusing the ability to start activities in the background. By default, apps are not allowed to bring the task stack to the foreground unless the creator allows background activity launch privileges or the sender has background activity launch privileges.
  • Control how the top activity of a task stack can finish its task. If the top activity finishes a task, Android will go back to whichever task was last active. Moreover, if a non-top activity finishes its task, Android will go back to the home screen; it won't block the finish of this non-top activity.
  • Prevent launching arbitrary activities from other apps into your own task. This change prevents malicious apps from phishing users by creating activities that appear to be from other apps.
  • Block non-visible windows from being considered for background activity launches. This helps prevent malicious apps from abusing background activity launches to display unwanted or malicious content to users.

Ý định an toàn hơn

Android 15 ra mắt các biện pháp bảo mật mới (không bắt buộc) để giúp ý định trở nên an toàn hơn và mạnh mẽ hơn. Những thay đổi này nhằm ngăn chặn các lỗ hổng tiềm ẩn và việc sử dụng sai ý định mà các ứng dụng độc hại có thể khai thác. Có hai điểm cải tiến chính về bảo mật của ý định trong Android 15:

  • So khớp bộ lọc ý định mục tiêu: Các ý định nhắm mục tiêu các thành phần cụ thể phải khớp chính xác với thông số kỹ thuật của bộ lọc ý định của mục tiêu. Nếu bạn gửi một ý định để khởi chạy hoạt động của một ứng dụng khác, thì thành phần ý định mục tiêu cần phải phù hợp với bộ lọc ý định đã khai báo của hoạt động nhận.
  • Ý định phải có hành động: Ý định không có hành động sẽ không còn khớp với bất kỳ bộ lọc ý định nào. Tức là các ý định dùng để bắt đầu hoạt động hoặc phải có hành động được xác định rõ ràng.

Để kiểm tra cách ứng dụng của bạn phản hồi những thay đổi này, hãy sử dụng StrictMode trong ứng dụng. Để xem nhật ký chi tiết về các lỗi vi phạm việc sử dụng Intent, hãy thêm 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 mang đến trải nghiệm người dùng nhất quán và trực quan hơn.

Thay đổi phần lồng ghép cửa sổ

Android 15 中与窗口内边距相关的两项变更:默认强制执行边到边,此外还有配置变更,例如系统栏的默认配置。

Thực thi chi tiết

Theo mặc định, các ứ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 (API cấp 35).

Một ứng dụng nhắm đến Android 14 và không hiển thị tràn viền trên thiết bị Android 15.


Một ứng dụng nhắm đến Android 15 (API cấp 35) và hiển thị tràn viền trên thiết bị Android 15. Ứng dụng này chủ yếu dùng các Thành phần Compose Material 3 tự động áp dụng phần lồng ghép. Màn hình này không chịu ảnh hưởng tiêu cực của biện pháp thực thi chế độ tràn viền trên Android 15.

Đây là một thay đổi có thể gây ả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 sau trên giao diện người dùng:

  • Thanh điều hướng bằng cử chỉ
    • Trong suốt theo mặc định.
    • Độ lệch dưới cùng bị vô hiệu hoá nên nội dung nằm phía sau thanh điều hướng của hệ thống, trừ phi bạn áp dụng phần lồng ghép.
    • setNavigationBarColorR.attr#navigationBarColor đã ngừng hoạt động và không ảnh hưởng đến chế độ thao tác bằng cử chỉ.
    • setNavigationBarContrastEnforcedR.attr#navigationBarContrastEnforced vẫn không ảnh hưởng đến chế độ thao tác bằng cử chỉ.
  • Thao tác bằng 3 nút
    • Độ mờ được đặt thành 80% theo mặc định, có thể có màu trùng 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 của hệ thống, trừ phi bạn áp dụng phần lồng ghép.
    • setNavigationBarColorR.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 chế độ mặc định này. API này không được dùng nữa nhưng vẫn ảnh hưởng đến chế độ thao tác bằng 3 nút.
    • setNavigationBarContrastEnforcedR.attr#navigationBarContrastEnforced theo mặc định là true, thêm nền không trong suốt 80% trên chế độ thao tác bằng 3 nút.
  • Thanh trạng thái
    • Trong suốt theo mặc định.
    • Độ lệch trên cùng bị vô hiệu hoá nên nội dung nằm phía sau thanh trạng thái, trừ phi bạn áp dụng phần lồng ghép.
    • setStatusBarColorR.attr#statusBarColor không được dùng nữa và không có hiệu lực trên Android 15.
    • setStatusBarContrastEnforcedR.attr#statusBarContrastEnforced không còn được dùng nữa nhưng vẫn có ảnh hưởng đến Android 15.
  • Vết cắt trên màn hình
    • layoutInDisplayCutoutMode của các cửa sổ không nổi phải là LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. SHORT_EDGES, NEVERDEFAULT đượ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 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 (API cấp 35), cũng như trước và sau khi áp dụng phần lồng ghép. Ví dụ này chưa đầy đủ và có thể xuất hiện khác trên Android Auto.

Một ứng dụng nhắm đến Android 14 và không hiển thị tràn viền trên thiết bị Android 15.
Một ứng dụng nhắm đến Android 15 (API cấp 35) và hiển thị tràn viền trên thiết bị Android 15. Tuy nhiên, nhiều phần tử hiện bị ẩn bởi thanh trạng thái, thanh điều hướng 3 nút hoặc vết cắt trên màn hình do các quy định thực thi chế độ hiển thị tràn viền trên Android 15. Giao diện người dùng ẩn bao gồm thanh ứng dụng trên cùng, nút hành động nổi và các mục trong danh sách của Material 2.
Một ứng dụng nhắm đến Android 15 (API cấp 35), hiển thị tràn viền trên thiết bị Android 15 và áp dụng phần lồng ghép để giao diện người dùng không bị ẩn.
Những điều cần kiểm tra nếu ứng dụng của bạn đã hiển thị tràn viền

Nếu ứng dụng của bạn đã hiển thị tràn viền và áp dụng phần lồng ghép, thì hầu hết bạn sẽ không bị ảnh hưởng, ngoại trừ trong các trường hợp sau. Tuy nhiên, ngay cả khi cho rằng ứng dụng của 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ư Activity sử dụng SHORT_EDGES, NEVER hoặc DEFAULT thay 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 1.2.0-alpha01 trở lên hoặc đặt window.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 bao gồm:
    • Màn hình hướng dẫn bắt đầu dùng ứng dụng 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 hiển thị tràn viền

Nếu ứng dụng của bạn chưa hiển thị tràn viền, thì rất có thể bạn sẽ bị ảnh hưởng. Ngoài các trường hợp dành cho những ứng dụng đã hiển thị tràn viền, bạn nên cân nhắc những trường hợp 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, BottomAppBarNavigationBar, thì những 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 đang 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 cho BottomAppBar, TopAppBar, BottomNavigationNavigationRail. Tương tự, hãy dùng tham số contentWindowInsets cho Scaffold.
  • Nếu ứng dụng của bạn dùng các khung hiển thị và Thành phần 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, NavigationRailView hoặc NavigationView) sẽ xử lý phần lồng ghép và không yêu cầu bạn làm gì thêm. Tuy nhiên, bạn cần thêm android:fitsSystemWindows="true" nếu dùng AppBarLayout.
  • Đố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 dưới dạng khoảng đệm. Nếu nội dung của bạn nằm trong Scaffold, bạn có thể sử dụng phần lồng ghép bằng cách dùng các giá trị khoảng đệm Scaffold. Nếu không, hãy áp dụng khoảng đệm bằng một trong các WindowInsets.
  • Nếu ứng dụng của bạn đang dùng khung hiển thị và BottomSheet, SideSheet hoặc các vùng chứa tuỳ chỉnh, hãy áp dụng khoảng đệm bằng ViewCompat.setOnApplyWindowInsetsListener. Đối với RecyclerView, hãy áp dụng khoảng đệm bằng trình nghe này, đồng thời thêm clipToPadding="false".
Những điều cần kiểm tra nếu ứng dụng của bạn phải cung cấp tính năng bảo vệ tuỳ chỉnh ở chế độ nền

Nếu ứng dụng của bạn phải cung cấp chế độ bảo vệ tuỳ chỉnh ở chế độ nền cho chế độ thao tác bằng 3 nút hoặc thanh trạng thái, thì ứng dụng của bạn nên đặt một thành phần kết hợp 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 thanh điều hướng bằng 3 nút hoặc WindowInsets.Type#statusBars.

Tài nguyên bổ sung về chế độ hiển thị tràn viền

Hãy xem hướng dẫn về Chế độ xem tràn viềnCompose 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á:

Các API sau đây không được dùng nữa và bị vô hiệu hoá:

Cấu hình ổn định

如果您的应用以 Android 15(API 级别 35)或更高版本为目标平台,Configuration 不再排除系统栏。如果您在 Configuration 类中使用屏幕尺寸进行布局计算,则应根据需要将其替换为更好的替代方案,例如适当的 ViewGroupWindowInsetsWindowMetricsCalculator

Configuration 自 API 1 起便已开始提供。它通常从 Activity.onConfigurationChanged 中获取。它提供窗口密度、方向和大小等信息。从 Configuration 返回的窗口大小的一个重要特征是,它之前排除了系统栏。

配置大小通常用于资源选择,例如 /res/layout-h500dp,这仍然是一个有效的使用情形。不过,我们一直不建议使用它进行布局计算。如果您正在这样做,请立即远离该设备。您应根据自己的使用场景,将 Configuration 的使用替换为更合适的用法。

如果您使用它来计算布局,请使用适当的 ViewGroup,例如 CoordinatorLayoutConstraintLayout。如果您使用它来确定系统导航栏的高度,请使用 WindowInsets。如果您想知道应用窗口的当前大小,请使用 computeCurrentWindowMetrics

以下列表介绍了受此变更影响的字段:

Thuộc tính elegantTextHeight mặc định là true

Đối với các ứng dụng nhắm đến Android 15 (API cấp 35), thuộc tính elegantTextHeight TextView sẽ trở thành true theo mặc định, thay thế phông chữ thu gọn được sử dụng theo mặc định bằng một số tập lệnh có các chỉ số dọc lớn bằng một tập lệnh dễ đọc hơn nhiều. Phông chữ nhỏ gọn được giới thiệu để ngăn các bố cục bị phá vỡ; Android 13 (API cấp 33) ngăn chặn nhiều sự cố này bằng cách cho phép bố cục văn bản kéo giãn chiều cao theo chiều dọc bằng cách sử dụng thuộc tính fallbackLineSpacing.

Trong Android 15, phông chữ thu gọn vẫn còn trong hệ thống, vì vậy, ứng dụng của bạn có thể đặt elegantTextHeight thành false để có cùng hành vi như trước, nhưng có thể sẽ không được hỗ trợ trong các bản phát hành sắp tới. Vì vậy, nếu ứng dụng của bạn hỗ trợ các tập lệnh sau: tiếng Ả Rập, tiếng Lào, tiếng Myanmar, tiếng Tamil, tiếng Gujarati, tiếng Kannada, tiếng Malayalam, tiếng Odia, tiếng Telugu hoặc tiếng Thái, hãy kiểm thử ứng dụng bằng cách đặt elegantTextHeight thành true.

Hành vi
elegantTextHeight của đối với các ứng dụng nhắm đến Android 14 (API cấp 34) trở xuống.
elegantTextHeight hành vi cho các ứng dụng nhắm đến Android 15.

Chiều rộng TextView thay đổi đối với các hình dạng chữ cái phức tạp

In previous versions of Android, some cursive fonts or languages that have complex shaping might draw the letters in the previous or next character's area. In some cases, such letters were clipped at the beginning or ending position. Starting in Android 15, a TextView allocates width for drawing enough space for such letters and allows apps to request extra paddings to the left to prevent clipping.

Because this change affects how a TextView decides the width, TextView allocates more width by default if the app targets Android 15 (API level 35) or higher. You can enable or disable this behavior by calling the setUseBoundsForWidth API on TextView.

Because adding left padding might cause a misalignment for existing layouts, the padding is not added by default even for apps that target Android 15 or higher. However, you can add extra padding to preventing clipping by calling setShiftDrawingOffsetForStartOverhang.

The following examples show how these changes can improve text layout for some fonts and languages.

Standard layout for English text in a cursive font. Some of the letters are clipped. Here is the corresponding XML:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
Layout for the same English text with additional width and padding. Here is the corresponding XML:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
Standard layout for Thai text. Some of the letters are clipped. Here is the corresponding XML:

<TextView
    android:text="คอมพิวเตอร์" />
Layout for the same Thai text with additional width and padding. Here is the corresponding XML:

<TextView
    android:text="คอมพิวเตอร์"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />

Chiều cao dòng mặc định theo ngôn ngữ cho EditText

Trong các phiên bản Android trước, bố cục văn bản đã kéo giãn chiều cao của văn bản để đáp ứng chiều cao dòng của phông chữ khớp với ngôn ngữ hiện tại. Ví dụ: nếu nội dung bằng tiếng Nhật, thì chiều cao dòng của phông chữ tiếng Nhật sẽ lớn hơn một chút so với chiều cao dòng của phông chữ Latinh, do đó chiều cao của văn bản sẽ lớn hơn một chút. Tuy nhiên, mặc dù có sự khác biệt về chiều cao dòng, nhưng phần tử EditText được định cỡ đồng nhất, bất kể ngôn ngữ đang được sử dụng, như minh hoạ trong hình sau:

Ba hộp đại diện cho các phần tử EditText có thể chứa văn bản bằng tiếng Anh (en), tiếng Nhật (ja) và tiếng Miến Điện (my). Chiều cao của EditText là như nhau, mặc dù các ngôn ngữ này có chiều cao dòng khác nhau.

Đối với các ứng dụng nhắm đến Android 15 (API cấp 35), chiều cao dòng tối thiểu hiện được dành riêng cho EditText để khớp với phông chữ tham chiếu cho Ngôn ngữ được chỉ định, như minh hoạ trong hình sau:

Ba hộp đại diện cho các phần tử EditText có thể chứa văn bản bằng tiếng Anh (en), tiếng Nhật (ja) và tiếng Miến Điện (my). Chiều cao của EditText hiện bao gồm khoảng trống để phù hợp với chiều cao dòng mặc định cho phông chữ của các ngôn ngữ này.

Nếu cần, ứng dụng của bạn có thể khôi phục hành vi trước đó bằng cách chỉ định thuộc tính useLocalePreferredLineHeightForMinimum thành false và ứng dụng có thể đặt các chỉ số dọc tối thiểu tuỳ chỉnh bằng API setMinimumFontMetrics trong Kotlin và Java.

Camera và nội dung nghe nhìn

Android 15 thực hiện những thay đổi sau đố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.

Quy định hạn chế đối với 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 接口的限制