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

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

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

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

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

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

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

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

Android 15 เปิดตัวลักษณะการหมดเวลาใหม่สำหรับ dataSync ในแอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) ขึ้นไป ลักษณะการทำงานนี้มีผลกับmediaProcessingบริการที่ทำงานอยู่เบื้องหน้าประเภทใหม่ด้วย

ระบบอนุญาตให้บริการ dataSync ของแอปทำงานเป็นเวลาทั้งหมด 6 ชั่วโมงในช่วงเวลา 24 ชั่วโมง หลังจากนั้นระบบจะเรียกใช้เมธอด Service.onTimeout(int, int) ของบริการที่ทำงานอยู่ (แนะนำใน Android 15) ขณะนี้บริการมีเวลา 2-3 วินาทีในการโทรไปที่ 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]"

หากต้องการหลีกเลี่ยงปัญหาการเปลี่ยนแปลงลักษณะการทำงานนี้ คุณสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้

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

หากบริการที่ทำงานอยู่เบื้องหน้า dataSync ของแอปทำงานเป็นเวลา 6 ชั่วโมงในช่วง 24 ชั่วโมงที่ผ่านมา คุณจะไม่สามารถเริ่มบริการที่ทำงานอยู่เบื้องหน้า 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 ขอแนะนำบริการที่ทำงานอยู่เบื้องหน้าประเภทใหม่ ได้แก่ 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

ก่อนหน้านี้ หากแอปมีสิทธิ์ 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

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

Apps that target Android 15 (API level 35) and higher can no longer change the global state or policy of Do Not Disturb (DND) on a device (either by modifying user settings, or turning off DND mode). Instead, apps must contribute an AutomaticZenRule, which the system combines into a global policy with the existing most-restrictive-policy-wins scheme. Calls to existing APIs that previously affected global state (setInterruptionFilter, setNotificationPolicy) result in the creation or update of an implicit AutomaticZenRule, which is toggled on and off depending on the call-cycle of those API calls.

Note that this change only affects observable behavior if the app is calling setInterruptionFilter(INTERRUPTION_FILTER_ALL) and expects that call to deactivate an AutomaticZenRule that was previously activated by their owners.

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

Android 15 ยังคงดำเนินการรีเฟรชไลบรารีหลักของ Android เพื่อปรับให้สอดคล้องกับฟีเจอร์ใน OpenJDK LTS เวอร์ชันล่าสุด

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

  • การเปลี่ยนแปลง API การจัดรูปแบบสตริง: ตอนนี้การตรวจสอบดัชนีอาร์กิวเมนต์ แฟล็ก ความกว้าง และความละเอียดจะเข้มงวดมากขึ้นเมื่อใช้ API String.format() และ Formatter.format() ต่อไปนี้

    ตัวอย่างเช่น ระบบจะแสดงข้อยกเว้นต่อไปนี้เมื่อใช้ดัชนีอาร์กิวเมนต์ 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]);
    
  • การเปลี่ยนแปลงการจัดการรหัสภาษา: เมื่อใช้ Locale API ระบบจะไม่แปลงรหัสภาษาสำหรับภาษาฮิบรู ยิดดิช และอินโดนีเซียเป็นรูปแบบที่ล้าสมัยอีกต่อไป (ฮิบรู: 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()

API SequencedCollection ใหม่อาจส่งผลต่อความเข้ากันได้ของแอปหลังจากคุณอัปเดต compileSdk ในการกำหนดค่าบิลด์ของแอปเพื่อใช้ Android 15 (API ระดับ 35)

  • การชนกับฟังก์ชันส่วนขยาย MutableList.removeFirst() และ MutableList.removeLast() ใน kotlin-stdlib

    ระบบจะแมปประเภท List ใน Java กับประเภท MutableList ใน Kotlin เนื่องจากมีการเปิดตัว API List.removeFirst() และ List.removeLast() ใน Android 15 (ระดับ API 35) คอมไพเลอร์ Kotlin จึงแก้ไขการเรียกใช้ฟังก์ชัน เช่น list.removeFirst() แบบคงที่ไปยัง API List ใหม่แทนที่จะแก้ไขไปยังฟังก์ชันส่วนขยายใน kotlin-stdlib

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

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

    ตัวเลือก NewApi lint ที่มีอยู่ในปลั๊กอิน Android Gradle สามารถตรวจจับการใช้ API ใหม่เหล่านี้ได้

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

    หากต้องการแก้ไขข้อยกเว้นรันไทม์และข้อผิดพลาดของ Lint ให้แทนที่การเรียกใช้ฟังก์ชัน removeFirst() และ removeLast() ด้วย removeAt(0) และ removeAt(list.lastIndex) ตามลำดับใน Kotlin หากคุณใช้ Android Studio Ladybug | 2024.1.3 ขึ้นไป โปรแกรมจะมีตัวเลือกการแก้ไขด่วนสำหรับข้อผิดพลาดเหล่านี้ด้วย

    ลองนำ @SuppressLint("NewApi") และ lintOptions { disable 'NewApi' } ออกหากปิดใช้ตัวเลือกการตรวจสอบโค้ด

  • การทับซ้อนกับเมธอดอื่นๆ ใน Java

    มีการเพิ่มเมธอดใหม่ในประเภทที่มีอยู่ เช่น List และ Deque วิธีการใหม่เหล่านี้อาจใช้ไม่ได้กับวิธีการที่มีชื่อและประเภทอาร์กิวเมนต์เดียวกันในอินเทอร์เฟซและคลาสอื่นๆ ในกรณีที่มีการทับซ้อนกันของลายเซ็นเมธอดที่ใช้งานร่วมกันไม่ได้ คอมไพเลอร์ javac จะแสดงข้อผิดพลาดของเวลาสร้าง เช่น

    ตัวอย่างข้อผิดพลาด 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
    

    ตัวอย่างข้อผิดพลาด 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
    

    ตัวอย่างข้อผิดพลาด 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
    

    หากต้องการแก้ไขข้อผิดพลาดเหล่านี้ในการสร้าง คลาสที่ใช้อินเทอร์เฟซเหล่านี้ควรลบล้างเมธอดที่มีประเภทผลลัพธ์ที่เข้ากันได้ เช่น

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

ความปลอดภัย

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

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

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

บล็อกแอปที่ไม่ตรงกับ UID ด้านบนในสแต็กไม่ให้เปิดใช้กิจกรรม

แอปที่เป็นอันตรายสามารถเปิดกิจกรรมของแอปอื่นภายในงานเดียวกันได้ จากนั้น วางซ้อนตัวเองทับด้านบน สร้างภาพลวงตาว่าเป็นแอปดังกล่าว งานนี้ "งาน" การปล้น" เจาะข้ามผ่านข้อจำกัด การเปิดตัวในเบื้องหลัง จะเกิดขึ้นภายในงานเดียวกันที่มองเห็น เพื่อลดความเสี่ยงนี้ Android 15 จึงเพิ่ม แฟล็กที่บล็อกแอปที่ไม่ตรงกับ UID บนสุดในสแต็กไม่ให้เริ่มทำงาน กิจกรรม หากต้องการเลือกใช้กิจกรรมทั้งหมดของแอป ให้อัปเดต allowCrossUidActivitySwitchFromBelow ในไฟล์ AndroidManifest.xml ของแอป

<application android:allowCrossUidActivitySwitchFromBelow="false" >

เราจะใช้มาตรการรักษาความปลอดภัยใหม่หากทุกเงื่อนไขต่อไปนี้เป็นจริง

  • แอปที่ดำเนินการเปิดตัวนั้นกำหนดเป้าหมายเป็น Android 15
  • แอปที่อยู่ด้านบนของสแต็กงานกำหนดเป้าหมายเป็น Android 15
  • กิจกรรมที่มองเห็นได้เลือกใช้การป้องกันใหม่

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

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

นอกจากข้อจำกัดสำหรับการจับคู่ UID แล้ว การเปลี่ยนแปลงอื่นๆ เหล่านี้ยัง รวม:

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

Intent ที่ปลอดภัยยิ่งขึ้น

Android 15 เปิดตัวมาตรการรักษาความปลอดภัยใหม่ที่ไม่บังคับเพื่อทำให้ Intent ปลอดภัยและมีประสิทธิภาพมากขึ้น การเปลี่ยนแปลงเหล่านี้มีจุดประสงค์เพื่อป้องกันช่องโหว่ที่อาจเกิดขึ้นและการใช้ Intent ในทางที่ผิดซึ่งแอปที่เป็นอันตรายอาจใช้ประโยชน์ได้ การปรับปรุงหลักๆ 2 ประการด้านความปลอดภัยของ Intent ใน Android 15 มีดังนี้

  • จับคู่ตัวกรอง Intent เป้าหมาย: Intent ที่กำหนดเป้าหมายคอมโพเนนต์ที่เฉพาะเจาะจงต้องตรงกับข้อกำหนดตัวกรอง Intent ของเป้าหมายอย่างถูกต้อง หากคุณส่งความตั้งใจที่จะเรียกใช้กิจกรรมของแอปอื่น คอมโพเนนต์ Intent เป้าหมายต้องสอดคล้องกับตัวกรอง Intent ที่ประกาศของกิจกรรมที่ได้รับ
  • Intent ต้องมีการดำเนินการ: Intent ที่ไม่มีการดำเนินการจะไม่จับคู่กับตัวกรอง Intent อีกต่อไป ซึ่งหมายความว่า Intent ที่ใช้เพื่อเริ่มกิจกรรมหรือบริการต้องมีการดำเนินการที่ระบุไว้อย่างชัดเจน

หากต้องการตรวจสอบว่าแอปของคุณตอบสนองต่อการเปลี่ยนแปลงเหล่านี้อย่างไร ให้ใช้ StrictMode ในแอป หากต้องการดูบันทึกโดยละเอียดเกี่ยวกับการละเมิดการใช้งาน Intent ให้เพิ่มเมธอดต่อไปนี้

Kotlin


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

Java


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

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

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

การเปลี่ยนแปลงช่วงกรอบเวลา

There are two changes related to window insets in Android 15: edge-to-edge is enforced by default, and there are also configuration changes, such as the default configuration of system bars.

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

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

แอปที่กำหนดเป้าหมายเป็น Android 14 และไม่ได้มีเนื้อหาแบบไร้ขอบในอุปกรณ์ Android 15


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

การเปลี่ยนแปลงนี้เป็นการเปลี่ยนแปลงที่อาจส่งผลเสียต่อ UI ของแอป การเปลี่ยนแปลงนี้จะส่งผลต่อส่วนต่างๆ ของ UI ต่อไปนี้

  • แถบนำทางของแฮนเดิลท่าทางสัมผัส
    • โปร่งใสโดยค่าเริ่มต้น
    • ระบบจะปิดใช้การเลื่อนด้านล่างเพื่อให้เนื้อหาวาดอยู่หลังแถบนําทางของระบบ เว้นแต่จะใช้การแทรก
    • setNavigationBarColor และ R.attr#navigationBarColor เลิกใช้งานแล้วและไม่ส่งผลต่อการนำทางด้วยท่าทางสัมผัส
    • setNavigationBarContrastEnforced และ R.attr#navigationBarContrastEnforced จะยังคงไม่มีผลต่อการนำทางด้วยท่าทางสัมผัส
  • การนำทางแบบ 3 ปุ่ม
    • ความทึบแสงจะตั้งไว้ที่ 80% โดยค่าเริ่มต้น โดยสีอาจตรงกับพื้นหลังของหน้าต่าง
    • ปิดใช้การเลื่อนด้านล่างเพื่อให้เนื้อหาวาดอยู่หลังแถบนำทางของระบบ เว้นแต่จะใช้การฝัง
    • setNavigationBarColor และ R.attr#navigationBarColor ได้รับการตั้งค่าให้เข้ากับพื้นหลังของหน้าต่างโดยค่าเริ่มต้น พื้นหลังหน้าต่างต้องเป็นสีที่ถอนออกได้เพื่อให้ใช้ค่าเริ่มต้นนี้ได้ 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 14 และไม่ได้แสดงแบบเต็มหน้าจอในอุปกรณ์ Android 15
แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) และเป็นแบบเต็มหน้าจอในอุปกรณ์ Android 15 อย่างไรก็ตาม ตอนนี้องค์ประกอบหลายอย่างจะซ่อนอยู่หลังแถบสถานะ แถบนําทาง 3 ปุ่ม หรือส่วนเว้าของจอแสดงผลเนื่องจากการบังคับใช้การแสดงผลแบบไร้ขอบของ Android 15 UI ที่ซ่อนอยู่ ได้แก่ แถบแอปด้านบน ปุ่มการดำเนินการแบบลอยตัว และรายการในรายการของ Material 2
แอปที่กำหนดเป้าหมายเป็น Android 15 (API ระดับ 35) แสดงเต็มหน้าจอในอุปกรณ์ Android 15 และใช้การฝังเพื่อให้ UI ไม่ซ่อนอยู่
สิ่งที่ต้องตรวจสอบหากแอปของคุณเป็นแบบเต็มหน้าจออยู่แล้ว

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

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

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

ทรัพยากรเพิ่มเติมแบบไร้ขอบ

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

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

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

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

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

หากแอปกำหนดเป้าหมายเป็น 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
  • Display.getMetrics() ทำงานแบบนี้มาตั้งแต่ API ระดับ 33

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

For apps targeting Android 15 (API level 35), the elegantTextHeight TextView attribute becomes true by default, replacing the compact font used by default with some scripts that have large vertical metrics with one that is much more readable. The compact font was introduced to prevent breaking layouts; Android 13 (API level 33) prevents many of these breakages by allowing the text layout to stretch the vertical height utilizing the fallbackLineSpacing attribute.

In Android 15, the compact font still remains in the system, so your app can set elegantTextHeight to false to get the same behavior as before, but it is unlikely to be supported in upcoming releases. So, if your app supports the following scripts: Arabic, Lao, Myanmar, Tamil, Gujarati, Kannada, Malayalam, Odia, Telugu or Thai, test your app by setting elegantTextHeight to true.

elegantTextHeight behavior for apps targeting Android 14 (API level 34) and lower.
elegantTextHeight behavior for apps targeting 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

In previous versions of Android, the text layout stretched the height of the text to meet the line height of the font that matched the current locale. For example, if the content was in Japanese, because the line height of the Japanese font is slightly larger than the one of a Latin font, the height of the text became slightly larger. However, despite these differences in line heights, the EditText element was sized uniformly, regardless of the locale being used, as illustrated in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText is the same, even though these languages have different line heights from each other.

For apps targeting Android 15 (API level 35), a minimum line height is now reserved for EditText to match the reference font for the specified Locale, as shown in the following image:

Three boxes representing EditText elements that can contain text from English (en), Japanese (ja), and Burmese (my). The height of the EditText now includes space to accommodate the default line height for these languages' fonts.

If needed, your app can restore the previous behavior by specifying the useLocalePreferredLineHeightForMinimum attribute to false, and your app can set custom minimum vertical metrics using the setMinimumFontMetrics API in Kotlin and Java.

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

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

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

Apps that target Android 15 (API level 35) must be the top app or running a foreground service in order to request audio focus. If an app attempts to request focus when it does not meet one of these requirements, the call returns AUDIOFOCUS_REQUEST_FAILED.

You can learn more about audio focus at Manage audio focus.

ข้อจำกัดที่ไม่ใช่ 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