การเปลี่ยนแปลงลักษณะการทำงาน: แอปที่กำหนดเป้าหมายเป็น Android 15 ขึ้นไป

Android 15 มีการเปลี่ยนแปลงลักษณะการทำงานที่อาจส่งผลต่อแอปของคุณเช่นเดียวกับรุ่นก่อนหน้า การเปลี่ยนแปลงลักษณะการทำงานต่อไปนี้จะมีผลกับแอปที่กำหนดเป้าหมายเป็น Android 15 ขึ้นไปเท่านั้น หากแอปกำหนดเป้าหมายเป็น Android 15 ขึ้นไป คุณควรแก้ไขแอปให้รองรับลักษณะการทำงานเหล่านี้อย่างเหมาะสมในกรณีที่ เกี่ยวข้อง

นอกจากนี้ โปรดตรวจสอบรายการการเปลี่ยนแปลงลักษณะการทำงานที่ส่งผลต่อแอปทั้งหมด ที่ทำงานบน Android 15 ไม่ว่า targetSdkVersion ของแอปจะเป็นอย่างไร

ฟังก์ชันหลัก

Android 15 จะแก้ไขหรือขยายความสามารถหลักต่างๆ ของระบบ Android

การเปลี่ยนแปลงบริการที่ทำงานอยู่เบื้องหน้า

เราจะทำการเปลี่ยนแปลงต่อไปนี้กับบริการที่ทำงานอยู่เบื้องหน้าใน Android 15

ลักษณะการหมดเวลาของบริการที่ทำงานอยู่เบื้องหน้าของการซิงค์ข้อมูล

Android 15 introduces a new timeout behavior to dataSync for apps targeting Android 15 (API level 35) or higher. This behavior also applies to the new mediaProcessing foreground service type.

The system permits an app's dataSync services to run for a total of 6 hours in a 24-hour period, after which the system calls the running service's Service.onTimeout(int, int) method (introduced in Android 15). At this time, the service has a few seconds to call Service.stopSelf(). When Service.onTimeout() is called, the service is no longer considered a foreground service. If the service does not call Service.stopSelf(), the system throws an internal exception. The exception is logged in Logcat with the following message:

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type dataSync did not stop within its timeout: [component name]"

To avoid problems with this behavior change, you can do one or more of the following:

  1. Have your service implement the new Service.onTimeout(int, int) method. When your app receives the callback, make sure to call stopSelf() within a few seconds. (If you don't stop the app right away, the system generates a failure.)
  2. Make sure your app's dataSync services don't run for more than a total of 6 hours in any 24-hour period (unless the user interacts with the app, resetting the timer).
  3. Only start dataSync foreground services as a result of direct user interaction; since your app is in the foreground when the service starts, your service has the full six hours after the app goes to the background.
  4. Instead of using a dataSync foreground service, use an alternative API.

If your app's dataSync foreground services have run for 6 hours in the last 24, you cannot start another dataSync foreground service unless the user has brought your app to the foreground (which resets the timer). If you try to start another dataSync foreground service, the system throws ForegroundServiceStartNotAllowedException with an error message like "Time limit already exhausted for foreground service type dataSync".

Testing

To test your app's behavior, you can enable data sync timeouts even if your app is not targeting Android 15 (as long as the app is running on an Android 15 device). To enable timeouts, run the following adb command:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

You can also adjust the timeout period, to make it easier to test how your app behaves when the limit is reached. To set a new timeout period, run the following adb command:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

ประเภทบริการที่ทำงานอยู่เบื้องหน้าใหม่สำหรับการประมวลผลสื่อ

Android 15 ขอแนะนำบริการที่ทำงานอยู่เบื้องหน้าประเภทใหม่ ได้แก่ mediaProcessing บริการประเภทนี้เหมาะสำหรับการดำเนินการต่างๆ เช่น การแปลงไฟล์สื่อ เช่น แอปสื่ออาจดาวน์โหลดไฟล์เสียงและต้องแปลงไฟล์เป็นรูปแบบอื่นก่อนเล่น คุณสามารถใช้mediaProcessingบริการที่ทำงานอยู่เบื้องหน้าเพื่อให้ Conversion ดำเนินต่อไปได้แม้ว่าแอปจะทำงานอยู่เบื้องหลัง

ระบบอนุญาตให้บริการ mediaProcessing ของแอปทำงานได้นาน 6 ชั่วโมงโดยรวมในระยะเวลา 24 ชั่วโมง หลังจากนั้นระบบจะเรียกใช้เมธอด Service.onTimeout(int, int) ของบริการที่ทำงานอยู่ (เปิดตัวใน Android 15) ขณะนี้บริการมีเวลา 2-3 วินาทีในการโทรไปที่ Service.stopSelf() หากบริการไม่ได้เรียกใช้ Service.stopSelf() ระบบจะแสดงข้อยกเว้นภายใน ระบบจะบันทึกข้อยกเว้นใน Logcat พร้อมข้อความต่อไปนี้

Fatal Exception: android.app.RemoteServiceException: "A foreground service of
type mediaProcessing did not stop within its timeout: [component name]"

หากต้องการหลีกเลี่ยงการมีข้อยกเว้น ให้ทําอย่างใดอย่างหนึ่งต่อไปนี้

  1. ให้บริการของคุณใช้วิธีการ Service.onTimeout(int, int) ใหม่ เมื่อแอปได้รับการติดต่อกลับ โปรดโทรหา stopSelf() ภายในไม่กี่วินาที (หากคุณไม่หยุดแอปในทันที ระบบจะล้มเหลว)
  2. ตรวจสอบว่าบริการ mediaProcessing ของแอปไม่ทำงานเป็นเวลานานกว่า 6 ชั่วโมงในทุกๆ 24 ชั่วโมง (เว้นแต่ผู้ใช้โต้ตอบกับแอป จะเป็นการรีเซ็ตตัวจับเวลา)
  3. เริ่มบริการที่ทำงานอยู่เบื้องหน้าmediaProcessingเฉพาะในกรณีที่มีการโต้ตอบโดยตรงจากผู้ใช้ เนื่องจากแอปของคุณอยู่เบื้องหน้าเมื่อบริการเริ่มทำงาน บริการของคุณจึงมีเวลา 6 ชั่วโมงเต็มหลังจากที่แอปเปลี่ยนไปทำงานในเบื้องหลัง
  4. ใช้ API อื่น เช่น WorkManager แทนบริการที่ทำงานอยู่เบื้องหน้าของ mediaProcessing

หากบริการที่ทำงานอยู่เบื้องหน้า mediaProcessing ของแอปทำงานเป็นเวลา 6 ชั่วโมงในช่วง 24 ชั่วโมงที่ผ่านมา คุณจะไม่สามารถเริ่มบริการที่ทำงานอยู่เบื้องหน้า mediaProcessing อื่นได้เว้นแต่ผู้ใช้จะนำแอปของคุณไปไว้ที่เบื้องหน้า (ซึ่งจะรีเซ็ตตัวจับเวลา) หากคุณพยายามเริ่มบริการที่ทำงานอยู่เบื้องหน้า mediaProcessing รายการอื่น ระบบจะแสดงForegroundServiceStartNotAllowedExceptionพร้อมข้อความแสดงข้อผิดพลาด เช่น "บริการที่ทำงานอยู่เบื้องหน้าประเภท mediaProcessing หมดเวลาแล้ว"

ดูข้อมูลเพิ่มเติมเกี่ยวกับประเภทบริการ mediaProcessing ได้ที่การเปลี่ยนแปลงประเภทบริการที่ทำงานอยู่เบื้องหน้าสำหรับ Android 15: การประมวลผลสื่อ

การทดสอบ

หากต้องการทดสอบลักษณะการทำงานของแอป ให้เปิดใช้ระยะหมดเวลาการประมวลผลสื่อ แม้ว่าแอปจะไม่ได้กำหนดเป้าหมายเป็น 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 media_processing_fgs_timeout_duration duration-in-milliseconds

ข้อจำกัดเกี่ยวกับ Broadcast Receiver BOOT_COMPLETED ที่เปิดบริการที่ทำงานอยู่เบื้องหน้า

มีข้อจำกัดใหม่ในการเปิดตัว Broadcast Receiver ของ BOOT_COMPLETED บริการที่ทำงานอยู่เบื้องหน้า ระบบไม่อนุญาตให้ Receiver 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

ข้อจำกัดในการเริ่มบริการที่ทำงานอยู่เบื้องหน้าขณะที่แอปมีสิทธิ์ SYSTEM_ALERT_WINDOW

Previously, if an app held the SYSTEM_ALERT_WINDOW permission, it could launch a foreground service even if the app was currently in the background (as discussed in exemptions from background start restrictions).

If an app targets Android 15, this exemption is now narrower. The app now needs to have the SYSTEM_ALERT_WINDOW permission and also have a visible overlay window. That is, the app needs to first launch a TYPE_APPLICATION_OVERLAY window and the window needs to be visible before you start a foreground service.

If your app attempts to start a foreground service from the background without meeting these new requirements (and it does not have some other exemption), the system throws ForegroundServiceStartNotAllowedException.

If your app declares the SYSTEM_ALERT_WINDOW permission and launches foreground services from the background, it may be affected by this change. If your app gets a ForegroundServiceStartNotAllowedException, check your app's order of operations and make sure your app already has an active overlay window before it attempts to start a foreground service from the background. You can check if your overlay window is currently visible by calling View.getWindowVisibility(), or you can override View.onWindowVisibilityChanged() to get notified whenever the visibility changes.

Testing

To test your app's behavior, you can enable these new restrictions even if your app is not targeting Android 15 (as long as the app is running on an Android 15 device). To enable these new restrictions on starting foreground services from the background, run the following adb command:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

การเปลี่ยนแปลงเวลาที่แอปจะแก้ไขสถานะส่วนกลางของโหมดห้ามรบกวนได้

แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ขึ้นไปจะไม่สามารถเปลี่ยนแปลงสถานะหรือนโยบายแบบรวมของโหมดห้ามรบกวน (DND) ในอุปกรณ์ได้อีกต่อไป (ไม่ว่าจะแก้ไขการตั้งค่าของผู้ใช้หรือปิดโหมด DND) แต่แอปต้องส่งAutomaticZenRule ซึ่งระบบจะรวมเข้ากับนโยบายส่วนกลางโดยใช้รูปแบบ "นโยบายที่เข้มงวดที่สุดจะชนะ" ที่มีอยู่ การเรียก API ที่มีอยู่ซึ่งก่อนหน้านี้ส่งผลต่อสถานะส่วนกลาง (setInterruptionFilter, setNotificationPolicy) จะส่งผลให้มีการสร้างหรืออัปเดต AutomaticZenRule ที่ไม่ชัดแจ้ง ซึ่งจะเปิดและปิดอยู่โดยขึ้นอยู่กับรอบการเรียกของ API เหล่านั้น

โปรดทราบว่าการเปลี่ยนแปลงนี้มีผลต่อลักษณะการทำงานที่สังเกตได้เฉพาะในกรณีที่แอปเรียกใช้ setInterruptionFilter(INTERRUPTION_FILTER_ALL) และคาดว่าการเรียกใช้ดังกล่าวจะปิดใช้งาน AutomaticZenRule ที่เจ้าของเปิดใช้งานไว้ก่อนหน้านี้

การเปลี่ยนแปลง API ของ OpenJDK

Android 15 continues the work of refreshing Android's core libraries to align with the features in the latest OpenJDK LTS releases.

Some of these changes can affect app compatibility for apps targeting Android 15 (API level 35):

  • Changes to string formatting APIs: Validation of argument index, flags, width, and precision are now more strict when using the following String.format() and Formatter.format() APIs:

    For example, the following exception is thrown when an argument index of 0 is used (%0 in the format string):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    In this case, the issue can be fixed by using an argument index of 1 (%1 in the format string).

  • Changes to component type of Arrays.asList(...).toArray(): When using Arrays.asList(...).toArray(), the component type of the resulting array is now an Object—not the type of the underlying array's elements. So the following code throws a ClassCastException:

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

    For this case, to preserve String as the component type in the resulting array, you could use Collection.toArray(Object[]) instead:

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Changes to language code handling: When using the Locale API, language codes for Hebrew, Yiddish, and Indonesian are no longer converted to their obsolete forms (Hebrew: iw, Yiddish: ji, and Indonesian: in). When specifying the language code for one of these locales, use the codes from ISO 639-1 instead (Hebrew: he, Yiddish: yi, and Indonesian: id).

  • Changes to random int sequences: Following the changes made in https://bugs.openjdk.org/browse/JDK-8301574, the following Random.ints() methods now return a different sequence of numbers than the Random.nextInt() methods do:

    Generally, this change shouldn't result in app-breaking behavior, but your code shouldn't expect the sequence generated from Random.ints() methods to match Random.nextInt().

The new SequencedCollection API can affect your app's compatibility after you update compileSdk in your app's build configuration to use Android 15 (API level 35):

  • Collision with MutableList.removeFirst() and MutableList.removeLast() extension functions in kotlin-stdlib

    The List type in Java is mapped to the MutableList type in Kotlin. Because the List.removeFirst() and List.removeLast() APIs have been introduced in Android 15 (API level 35), the Kotlin compiler resolves function calls, for example list.removeFirst(), statically to the new List APIs instead of to the extension functions in kotlin-stdlib.

    If an app is re-compiled with compileSdk set to 35 and minSdk set to 34 or lower, and then the app is run on Android 14 and lower, a runtime error is thrown:

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

    The existing NewApi lint option in Android Gradle Plugin can catch these new API usages.

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

    To fix the runtime exception and lint errors, the removeFirst() and removeLast() function calls can be replaced with removeAt(0) and removeAt(list.lastIndex) respectively in Kotlin. If you're using Android Studio Ladybug | 2024.1.3 or higher, it also provides a quick fix option for these errors.

    Consider removing @SuppressLint("NewApi") and lintOptions { disable 'NewApi' } if the lint option has been disabled.

  • Collision with other methods in Java

    New methods have been added into the existing types, for example, List and Deque. These new methods might not be compatible with the methods with the same name and argument types in other interfaces and classes. In the case of a method signature collision with incompatibility, the javac compiler outputs a build-time error. For example:

    Example error 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
    

    Example error 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
    

    Example error 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
    

    To fix these build errors, the class implementing these interfaces should override the method with a compatible return type. For example:

    @Override
    public Object getFirst() {
        return List.super.getFirst();
    }
    

ความปลอดภัย

Android 15 มีการเปลี่ยนแปลงที่ส่งเสริมความปลอดภัยของระบบเพื่อช่วยปกป้องแอป และผู้ใช้จากแอปที่เป็นอันตราย

เวอร์ชัน TLS ที่ถูกจำกัด

Android 15 จำกัดการใช้ TLS เวอร์ชัน 1.0 และ 1.1 ก่อนหน้านี้ Android ได้เลิกใช้งานเวอร์ชันเหล่านี้แล้ว แต่ตอนนี้ไม่อนุญาตให้แอปที่กําหนดเป้าหมายเป็น Android 15 ใช้เวอร์ชันดังกล่าว

เปิดใช้กิจกรรมในเบื้องหลังที่ปลอดภัย

Android 15 ปกป้องผู้ใช้จากแอปที่เป็นอันตรายและให้ผู้ใช้ควบคุมอุปกรณ์ได้มากขึ้น โดยการเพิ่มการเปลี่ยนแปลงที่ป้องกันไม่ให้แอปที่เป็นอันตรายซึ่งทำงานในเบื้องหลัง นำแอปอื่นๆ มาไว้ที่เบื้องหน้า ยกระดับสิทธิ์ และละเมิด การโต้ตอบของผู้ใช้ การเปิดใช้กิจกรรมในเบื้องหลังถูกจำกัดตั้งแต่ Android 10 (API ระดับ 29)

การเปลี่ยนแปลงอื่นๆ

  • เปลี่ยนให้ครีเอเตอร์ PendingIntent บล็อกการเปิดใช้กิจกรรมในเบื้องหลังโดย ค่าเริ่มต้น ซึ่งจะช่วยป้องกันไม่ให้แอปสร้างPendingIntentโดยไม่ได้ตั้งใจ ซึ่งผู้ไม่ประสงค์ดีอาจนำไปใช้ในทางที่ผิดได้
  • อย่านำแอปมาไว้เบื้องหน้า เว้นแต่PendingIntentผู้ส่ง จะอนุญาต การเปลี่ยนแปลงนี้มีจุดมุ่งหมายเพื่อป้องกันไม่ให้แอปที่เป็นอันตรายใช้ความสามารถในการเริ่มกิจกรรมในเบื้องหลังในทางที่ผิด โดยค่าเริ่มต้น แอปจะไม่ได้รับอนุญาตให้นำสแต็กงานมาไว้ที่เบื้องหน้า เว้นแต่ผู้สร้างจะอนุญาตสิทธิ์ในการเปิดกิจกรรมในเบื้องหลัง หรือผู้ส่งมีสิทธิ์ในการเปิดกิจกรรมในเบื้องหลัง
  • ควบคุมวิธีที่กิจกรรมบนสุดของสแต็กงานจะทำงานให้เสร็จ หากกิจกรรมที่อยู่ด้านบนสุดทำงานเสร็จแล้ว Android จะกลับไปที่งานที่ใช้งานล่าสุด นอกจากนี้ หากกิจกรรมที่ไม่ใช่กิจกรรมที่อยู่ด้านบนสุดทำงานเสร็จ Android จะ กลับไปที่หน้าจอหลัก และจะไม่บล็อกการสิ้นสุดของกิจกรรมที่ไม่ใช่กิจกรรมที่อยู่ด้านบนสุดนี้
  • ป้องกันไม่ให้แอปอื่นๆ เปิดกิจกรรมที่กำหนดเองในงานของคุณ การเปลี่ยนแปลงนี้จะป้องกันไม่ให้แอปที่เป็นอันตรายฟิชชิงผู้ใช้โดยการสร้าง กิจกรรมที่ดูเหมือนมาจากแอปอื่นๆ
  • บล็อกไม่ให้ระบบพิจารณาหน้าต่างที่มองไม่เห็นสำหรับการเปิดใช้กิจกรรมในเบื้องหลัง ซึ่งจะช่วยป้องกันไม่ให้แอปที่เป็นอันตรายใช้การเปิดใช้งานในเบื้องหลังในทางที่ผิดเพื่อแสดงเนื้อหาที่ไม่พึงประสงค์หรือเนื้อหาที่เป็นอันตรายต่อผู้ใช้

Intent ที่ปลอดภัยกว่า

Android 15 introduces StrictMode for intents.

In order to see detailed logs about Intent usage violations, use following method:

Kotlin

fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}

Java

public void onCreate() {
    StrictMode.setVmPolicy(new VmPolicy.Builder()
            .detectUnsafeIntentLaunch()
            .build());
}

ประสบการณ์ของผู้ใช้และ UI ของระบบ

Android 15 มีการเปลี่ยนแปลงบางอย่างที่มีจุดประสงค์เพื่อสร้างประสบการณ์ของผู้ใช้ที่สอดคล้องกันมากขึ้น และใช้งานง่าย

การเปลี่ยนแปลงส่วนที่เว้นไว้ในหน้าต่าง

การเปลี่ยนแปลงที่เกี่ยวข้องกับส่วนแทรกของหน้าต่างใน Android 15 มี 2 อย่าง ได้แก่ ระบบจะบังคับใช้การแสดงผลแบบเต็มหน้าจอโดยค่าเริ่มต้น และยังมีการเปลี่ยนแปลงการกำหนดค่า เช่น การกําหนดค่าเริ่มต้นของแถบระบบ

การบังคับใช้แบบไร้ขอบ

แอปจะแสดงผลแบบไร้ขอบโดยค่าเริ่มต้นในอุปกรณ์ที่ใช้ Android 15 หากแอปกำหนดเป้าหมายเป็น Android 15 (API ระดับ 35)

งานที่ต้องทำเพื่อให้ใช้งานร่วมกับการบังคับใช้แบบขอบจรดขอบของ Android 15 ได้
แอปที่กำหนดเป้าหมายเป็น Android 14 และไม่ได้แสดงผลแบบไร้ขอบใน อุปกรณ์ Android 15


แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) และเป็นแบบขอบจรดขอบ ในอุปกรณ์ Android 15 แอปนี้ส่วนใหญ่ใช้คอมโพเนนต์ Compose ของ Material 3 ซึ่งจะใช้ระยะขอบโดยอัตโนมัติ หน้าจอนี้ไม่ได้รับผลกระทบในทางลบจาก การบังคับใช้แบบไร้ขอบของ Android 15

นี่เป็นการเปลี่ยนแปลงที่ไม่รองรับการทำงานร่วมกันซึ่งอาจส่งผลเสียต่อ UI ของแอป การเปลี่ยนแปลงนี้ส่งผลต่อพื้นที่ UI ต่อไปนี้

  • แถบนำทางด้วยแฮนเดิลท่าทางสัมผัส
    • โปร่งใสโดยค่าเริ่มต้น
    • ปิดใช้การชดเชยด้านล่างเพื่อให้เนื้อหาแสดงอยู่ด้านหลังแถบนำทางของระบบ เว้นแต่จะใช้ Inset
    • setNavigationBarColor และ R.attr#navigationBarColor เลิกใช้งานแล้วและไม่ส่งผลต่อการไปยังส่วนต่างๆ ด้วยท่าทางสัมผัส
    • setNavigationBarContrastEnforced และ R.attr#navigationBarContrastEnforced จะยังคงไม่มีผลต่อ การนำทางด้วยท่าทางสัมผัส
  • การนำทางแบบ 3 ปุ่ม
    • ความทึบแสงตั้งค่าเป็น 80% โดยค่าเริ่มต้น โดยสีอาจตรงกับพื้นหลังของหน้าต่าง
    • ปิดใช้การชดเชยด้านล่างเพื่อให้เนื้อหาแสดงอยู่ด้านหลังแถบนำทางของระบบ เว้นแต่จะใช้ Inset
    • setNavigationBarColor และ R.attr#navigationBarColor จะ ตั้งค่าให้ตรงกับพื้นหลังของหน้าต่างโดยค่าเริ่มต้น พื้นหลังของหน้าต่าง ต้องเป็น Drawable สีเพื่อให้ค่าเริ่มต้นนี้มีผล API นี้ เลิกใช้งานแล้ว แต่ยังคงส่งผลต่อการนำทางด้วย 3 ปุ่ม
    • setNavigationBarContrastEnforced และ R.attr#navigationBarContrastEnforced เป็นจริงโดยค่าเริ่มต้น ซึ่งจะเพิ่มพื้นหลังทึบแสง 80% ในการนำทางแบบ 3 ปุ่ม
  • แถบสถานะ
    • โปร่งใสโดยค่าเริ่มต้น
    • ระบบจะปิดใช้การชดเชยด้านบนเพื่อให้เนื้อหาแสดงอยู่ด้านหลังแถบสถานะ เว้นแต่จะมีการใช้ ระยะขอบ
    • setStatusBarColor และ R.attr#statusBarColor เลิกใช้งานแล้วและจะไม่มีผลกับ Android 15
    • setStatusBarContrastEnforced และ R.attr#statusBarContrastEnforced ถูกเลิกใช้งานแล้ว แต่ยังคงส่งผลต่อ Android 15
  • รอยบากบนจอแสดงผล
    • layoutInDisplayCutoutMode ของหน้าต่างที่ไม่ลอยต้องเป็น LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS ระบบจะตีความ SHORT_EDGES, NEVER และ DEFAULT เป็น ALWAYS เพื่อให้ผู้ใช้ไม่เห็นแถบสีดำ ที่เกิดจากการเว้นขอบจอแสดงผล และปรากฏแบบขอบจรดขอบ

ตัวอย่างต่อไปนี้แสดงแอปก่อนและหลังการกำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) รวมถึงก่อนและหลังการใช้ส่วนที่เว้นไว้ ตัวอย่างนี้ไม่ครอบคลุมทั้งหมด และอาจปรากฏแตกต่างกันใน Android Auto

แอปที่กำหนดเป้าหมายเป็น Android 14 และไม่ได้แสดงผลแบบไร้ขอบใน อุปกรณ์ Android 15
แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) และเป็นแบบขอบจรดขอบ ในอุปกรณ์ Android 15 อย่างไรก็ตาม ตอนนี้แถบสถานะ แถบนำทางแบบ 3 ปุ่ม หรือรอยบากของจอแสดงผลจะซ่อนองค์ประกอบหลายอย่างเนื่องจากการบังคับใช้แบบไร้ขอบของ Android 15 UI ที่ซ่อนอยู่ประกอบด้วยแถบแอปด้านบนของ Material 2 ปุ่มลอย และรายการ
แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) จะแสดงแบบขอบถึงขอบใน อุปกรณ์ Android 15 และใช้ระยะขอบเพื่อให้ UI ไม่ถูกซ่อน
สิ่งที่ต้องตรวจสอบหากแอปเป็นแบบขอบจรดขอบอยู่แล้ว

หากแอปเป็นแบบขอบจรดขอบอยู่แล้วและใช้ Inset คุณจะได้รับผลกระทบน้อยมาก ยกเว้นในสถานการณ์ต่อไปนี้ อย่างไรก็ตาม แม้ว่าคุณจะคิดว่าไม่ได้รับผลกระทบ เราขอแนะนำให้ทดสอบแอป

  • คุณมีหน้าต่างที่ไม่ลอย เช่น Activity ที่ใช้ SHORT_EDGES, NEVER หรือ DEFAULT แทน LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS หากแอปขัดข้องเมื่อเปิดใช้งาน ปัญหานี้ อาจเกิดจากหน้าจอเริ่มต้น คุณจะอัปเกรดการขึ้นต่อcore splashscreen เป็น 1.2.0-alpha01 หรือใหม่กว่า หรือตั้งค่า window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always ก็ได้
  • อาจมีหน้าจอที่มีการเข้าชมต่ำซึ่งมี UI ที่ถูกบดบัง ตรวจสอบว่าหน้าจอที่มีการเข้าชมน้อยกว่าเหล่านี้ไม่มี UI ที่ถูกบดบัง หน้าจอที่มีการเข้าชมต่ำ ได้แก่
    • หน้าจอเริ่มต้นใช้งานหรือลงชื่อเข้าใช้
    • หน้าการตั้งค่า
สิ่งที่ต้องตรวจสอบหากแอปยังไม่แสดงแบบขอบจรดขอบ

หากแอปของคุณยังไม่ได้แสดงผลแบบขอบถึงขอบ คุณอาจได้รับผลกระทบ นอกเหนือจากสถานการณ์สำหรับแอปที่แสดงแบบขอบจรดขอบอยู่แล้ว คุณควรพิจารณาสิ่งต่อไปนี้

  • หากแอปใช้คอมโพเนนต์ Material 3 ( androidx.compose.material3) ใน Compose เช่น TopAppBar, BottomAppBar และ NavigationBar คอมโพเนนต์เหล่านี้ไม่ น่าจะได้รับผลกระทบเนื่องจากจะจัดการระยะขอบโดยอัตโนมัติ
  • หากแอปใช้คอมโพเนนต์ Material 2 ( androidx.compose.material) ใน Compose คอมโพเนนต์เหล่านี้ จะไม่จัดการระยะขอบโดยอัตโนมัติ อย่างไรก็ตาม คุณสามารถเข้าถึงระยะขอบ และใช้ระยะขอบด้วยตนเองได้ ใน androidx.compose.material 1.6.0 ขึ้นไป ให้ใช้พารามิเตอร์ windowInsets เพื่อใช้ Inset ด้วยตนเองสำหรับ BottomAppBar, TopAppBar, BottomNavigation และ NavigationRail เช่นเดียวกัน ให้ใช้พารามิเตอร์ contentWindowInsets สำหรับ Scaffold
  • หากแอปใช้ View และคอมโพเนนต์ Material (com.google.android.material) คอมโพเนนต์ Material ส่วนใหญ่ที่อิงตาม View เช่น BottomNavigationView, BottomAppBar, NavigationRailView หรือ NavigationView จะจัดการระยะขอบภายในและไม่จำเป็นต้องดำเนินการเพิ่มเติม แต่คุณต้องเพิ่ม android:fitsSystemWindows="true" หากใช้ AppBarLayout
  • สำหรับ Composable ที่กำหนดเอง ให้ใช้ Inset เป็น Padding ด้วยตนเอง หากเนื้อหาอยู่ภายใน Scaffold คุณจะใช้ขอบในได้โดยใช้Scaffold ค่าระยะห่างภายใน หรือใช้ระยะห่างโดยใช้หนึ่งใน WindowInsets
  • หากแอปของคุณใช้มุมมองและ BottomSheet, SideSheet หรือคอนเทนเนอร์ที่กำหนดเอง ให้ใช้ระยะห่างภายในโดยใช้ ViewCompat.setOnApplyWindowInsetsListener สำหรับ RecyclerView ให้ใช้ระยะห่างจากขอบโดยใช้ Listener นี้ และเพิ่ม clipToPadding="false" ด้วย
สิ่งที่ต้องตรวจสอบหากแอปต้องมีการคุ้มครองเบื้องหลังที่กำหนดเอง

หากแอปต้องมีการป้องกันพื้นหลังที่กำหนดเองสำหรับการนำทางด้วย 3 ปุ่มหรือแถบสถานะ แอปควรวาง Composable หรือ View ไว้ด้านหลังแถบระบบโดยใช้ WindowInsets.Type#tappableElement() เพื่อรับความสูงของแถบนำทางด้วย 3 ปุ่มหรือ WindowInsets.Type#statusBars

แหล่งข้อมูลเพิ่มเติมแบบขอบจรดขอบ

ดูข้อควรพิจารณาเพิ่มเติมเกี่ยวกับการใช้ระยะขอบได้ในคำแนะนำมุมมองแบบขอบจรดขอบและการเขียนแบบขอบจรดขอบ

API ที่เลิกใช้งานแล้ว

API ต่อไปนี้เลิกใช้งานแล้วแต่ยังไม่ได้ปิดใช้

API ต่อไปนี้เลิกใช้งานแล้วและถูกปิดใช้

การกำหนดค่าที่เสถียร

If your app targets Android 15 (API level 35) or higher, Configuration no longer excludes the system bars. If you use the screen size in the Configuration class for layout calculation, you should replace it with better alternatives like an appropriate ViewGroup, WindowInsets, or WindowMetricsCalculator depending on your need.

Configuration has been available since API 1. It is typically obtained from Activity.onConfigurationChanged. It provides information like window density, orientation, and sizes. One important characteristic about the window sizes returned from Configuration is that it previously excluded the system bars.

The configuration size is typically used for resource selection, such as /res/layout-h500dp, and this is still a valid use case. However, using it for layout calculation has always been discouraged. If you do so, you should move away from it now. You should replace the use of Configuration with something more suitable depending on your use case.

If you use it to calculate the layout, use an appropriate ViewGroup, such as CoordinatorLayout or ConstraintLayout. If you use it to determine the height of the system navbar, use WindowInsets. If you want to know the current size of your app window, use computeCurrentWindowMetrics.

The following list describes the fields affected by this change:

แอตทริบิวต์ elegantTextHeight จะมีค่าเริ่มต้นเป็น true

สําหรับแอปที่กําหนดเป้าหมายเป็น Android 15 (API ระดับ 35) แอตทริบิวต์ elegantTextHeight TextView จะเปลี่ยนเป็น true โดยค่าเริ่มต้น โดยแทนที่แบบอักษรแบบกะทัดรัดที่ใช้โดยค่าเริ่มต้นด้วยสคริปต์บางรายการที่มีเมตริกแนวตั้งขนาดใหญ่ด้วยแบบอักษรที่อ่านง่ายกว่ามาก แบบอักษรแบบกะทัดรัดมีไว้เพื่อป้องกันการแบ่งเลย์เอาต์ Android 13 (API ระดับ 33) ป้องกันปัญหาการแบ่งเลย์เอาต์เหล่านี้ได้หลายอย่างโดยอนุญาตให้เลย์เอาต์ข้อความยืดความสูงแนวตั้งโดยใช้แอตทริบิวต์ fallbackLineSpacing

ใน Android 15 ฟอนต์แบบกะทัดรัดจะยังคงอยู่ในระบบ แอปของคุณจึงตั้งค่า elegantTextHeight เป็น false เพื่อให้มีลักษณะการทำงานเหมือนเดิมได้ แต่ไม่น่าจะได้รับการสนับสนุนในรุ่นที่กำลังจะเปิดตัว ดังนั้น หากแอปของคุณรองรับสคริปต์ต่อไปนี้ อาหรับ ลาว พม่า ทมิฬ คุชราต กันนาดา มาลายาลัม โอเดีย เตลูกู หรือไทย ให้ทดสอบแอปโดยตั้งค่า elegantTextHeight เป็น true

ลักษณะการทํางานของ
elegantTextHeight สําหรับแอปที่กําหนดเป้าหมายเป็น Android 14 (API ระดับ 34) และต่ำกว่า
ลักษณะการทํางานของ
elegantTextHeight สําหรับแอปที่กําหนดเป้าหมายเป็น Android 15

การเปลี่ยนแปลงความกว้างของ TextView สำหรับรูปร่างตัวอักษรที่ซับซ้อน

ใน Android เวอร์ชันเก่า แบบอักษรตัวเขียนบางแบบหรือภาษาที่มีรูปร่างซับซ้อนอาจวาดตัวอักษรในพื้นที่ของตัวอักษรก่อนหน้าหรือถัดไปตามลำดับ ในบางกรณี ตัวอักษรเหล่านั้นอาจถูกตัดออกตั้งแต่จุดเริ่มต้นหรือจุดสิ้นสุด ตั้งแต่ Android 15 เป็นต้นไป TextView จะจัดสรรความกว้างสำหรับการวาดตัวอักษรดังกล่าวให้มีพื้นที่เพียงพอ และอนุญาตให้แอปขอการถ่วงข้อความเพิ่มเติมทางด้านซ้ายเพื่อป้องกันการตัดข้อความ

เนื่องจากการเปลี่ยนแปลงนี้ส่งผลต่อวิธีที่ TextView กำหนดความกว้าง TextView จะจัดสรรความกว้างเพิ่มเติมโดยค่าเริ่มต้นหากแอปกำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ขึ้นไป คุณเปิดหรือปิดใช้ลักษณะการทำงานนี้ได้โดยเรียกใช้ setUseBoundsForWidth API ใน TextView

เนื่องจากการเพิ่มระยะห่างจากขอบด้านซ้ายอาจทําให้เลย์เอาต์ที่มีอยู่ไม่สอดคล้องกัน ระบบจึงไม่เพิ่มระยะห่างจากขอบโดยค่าเริ่มต้น แม้แต่สําหรับแอปที่กําหนดเป้าหมายเป็น Android 15 ขึ้นไป อย่างไรก็ตาม คุณสามารถเพิ่มการเว้นวรรคเพิ่มเติมเพื่อป้องกันการตัดได้โดยเรียกใช้ setShiftDrawingOffsetForStartOverhang

ตัวอย่างต่อไปนี้แสดงให้เห็นว่าการเปลี่ยนแปลงเหล่านี้ช่วยปรับปรุงเลย์เอาต์ข้อความสำหรับแบบอักษรและภาษาบางแบบได้อย่างไร

เลย์เอาต์มาตรฐานสำหรับข้อความภาษาอังกฤษในแบบอักษรที่ตัวห้อย ตัวอักษรบางตัวถูกตัด XML ที่เกี่ยวข้องมีดังนี้

<TextView
    android:fontFamily="cursive"
    android:text="java" />
เลย์เอาต์สำหรับข้อความภาษาอังกฤษเดียวกันนี้โดยเพิ่มความกว้างและการเว้นวรรค XML ที่เกี่ยวข้องมีดังนี้

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
เลย์เอาต์มาตรฐานสำหรับข้อความภาษาไทย ตัวอักษรบางตัวถูกตัด XML ที่เกี่ยวข้องมีดังนี้

<TextView
    android:text="คอมพิวเตอร์" />
เลย์เอาต์สำหรับข้อความภาษาไทยเดียวกันนี้ที่มีความกว้างและระยะห่างจากขอบเพิ่มเติม นี่คือ XML ที่เกี่ยวข้อง:

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

ความสูงของบรรทัดเริ่มต้นที่รับรู้ภาษาสำหรับ EditText

ใน Android เวอร์ชันก่อนหน้า เลย์เอาต์ข้อความจะยืดความสูงของข้อความให้เท่ากับความสูงของบรรทัดแบบอักษรที่ตรงกับภาษาปัจจุบัน เช่น หากเนื้อหาเป็นภาษาญี่ปุ่น ความสูงของบรรทัดของแบบอักษรญี่ปุ่นจะสูงกว่าแบบอักษรละตินเล็กน้อย ความสูงของข้อความจึงเพิ่มขึ้นเล็กน้อย อย่างไรก็ตาม แม้ความสูงของบรรทัดจะแตกต่างกัน แต่องค์ประกอบ EditText ก็มีการปรับขนาดให้เหมือนกัน โดยไม่คำนึงถึงภาษาที่ใช้ ดังที่แสดงในภาพต่อไปนี้

กล่อง 3 กล่องที่แสดงองค์ประกอบ EditText ซึ่งอาจมีข้อความจากภาษาอังกฤษ (en), ญี่ปุ่น (ja) และพม่า (my) ความสูงของ EditText เหมือนกัน แม้ว่าภาษาเหล่านี้จะมีความสูงของบรรทัดต่างกัน

สำหรับแอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ระบบจะสงวนความสูงบรรทัดขั้นต่ำไว้สำหรับ EditText เพื่อให้ตรงกับแบบอักษรอ้างอิงสำหรับภาษาที่ระบุ ดังที่แสดงในภาพต่อไปนี้

กล่อง 3 กล่องที่แสดงองค์ประกอบ EditText ซึ่งอาจมีข้อความจากภาษาอังกฤษ (en), ญี่ปุ่น (ja) และพม่า (my) ตอนนี้ความสูงของ EditText จะรวมช่องว่างไว้เพื่อรองรับความสูงของบรรทัดเริ่มต้นสำหรับแบบอักษรของภาษาเหล่านี้

หากจําเป็น แอปจะกู้คืนลักษณะการทํางานก่อนหน้าได้โดยระบุแอตทริบิวต์ useLocalePreferredLineHeightForMinimum เป็น false และแอปจะตั้งค่าเมตริกแนวตั้งขั้นต่ำที่กําหนดเองได้โดยใช้ setMinimumFontMetrics API ใน Kotlin และ Java

กล้องและสื่อ

Android 15 จะทำการเปลี่ยนแปลงลักษณะการทำงานของกล้องและสื่อสำหรับแอปที่กำหนดเป้าหมายเป็น Android 15 ขึ้นไปดังนี้

ข้อจำกัดในการขอโฟกัสเสียง

แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ต้องเป็นแอปที่ทำงานอยู่ด้านบนหรือกำลังใช้บริการที่ทำงานอยู่เบื้องหน้าเพื่อขอโฟกัสเสียง หากแอปพยายามขอโฟกัสเมื่อไม่เป็นไปตามข้อกำหนดข้อใดข้อหนึ่งเหล่านี้ การเรียกใช้จะแสดงผลเป็น AUDIOFOCUS_REQUEST_FAILED

ดูข้อมูลเพิ่มเติมเกี่ยวกับโหมดโฟกัสเสียงได้ที่จัดการโหมดโฟกัสเสียง

ข้อจำกัดที่ไม่ใช่ SDK ที่อัปเดตแล้ว

Android 15 มีรายการอัปเดตของอินเทอร์เฟซที่ไม่ใช่ SDK ซึ่งถูกจำกัด โดยการทำงานร่วมกับนักพัฒนาแอป Android และการทดสอบภายในล่าสุด เราจะตรวจสอบว่ามีทางเลือกอื่นที่เผยแพร่ต่อสาธารณะพร้อมใช้งานก่อนที่จะจำกัดอินเทอร์เฟซที่ไม่ใช่ SDK ทุกครั้งที่ทำได้

หากแอปไม่ได้กำหนดเป้าหมายเป็น Android 15 การเปลี่ยนแปลงบางอย่างเหล่านี้ อาจไม่มีผลกับคุณในทันที อย่างไรก็ตาม แม้ว่าแอปของคุณจะ เข้าถึงอินเทอร์เฟซที่ไม่ใช่ SDK บางรายการได้ ขึ้นอยู่กับระดับ API เป้าหมายของแอป แต่การใช้เมธอดหรือฟิลด์ที่ไม่ใช่ SDK ใดๆ ก็มีความเสี่ยงสูงที่จะทำให้แอปขัดข้องเสมอ

หากไม่แน่ใจว่าแอปใช้อินเทอร์เฟซที่ไม่ใช่ SDK หรือไม่ คุณสามารถทดสอบแอปเพื่อดูได้ หากแอปของคุณใช้อินเทอร์เฟซที่ไม่ใช่ SDK คุณควรเริ่มวางแผนย้ายไปใช้ทางเลือกอื่นที่เป็น SDK อย่างไรก็ตาม เราเข้าใจว่าแอปบางแอปมี Use Case ที่ถูกต้องสำหรับการใช้ อินเทอร์เฟซที่ไม่ใช่ SDK หากไม่พบวิธีอื่นแทนการใช้อินเทอร์เฟซที่ไม่ใช่ SDK สำหรับฟีเจอร์ในแอป คุณควรขอ API สาธารณะใหม่

ดูข้อมูลเพิ่มเติมเกี่ยวกับการเปลี่ยนแปลงใน Android เวอร์ชันนี้ได้ที่การอัปเดตข้อจำกัดของอินเทอร์เฟซที่ไม่ใช่ SDK ใน Android 15 ดูข้อมูลเพิ่มเติมเกี่ยวกับอินเทอร์เฟซที่ไม่ใช่ SDK โดยทั่วไปได้ที่ข้อจำกัดเกี่ยวกับอินเทอร์เฟซที่ไม่ใช่ SDK