Zmiany w działaniu: aplikacje kierowane na Androida 15 lub nowszego

Podobnie jak w przypadku poprzednich wersji Androida, w Androidzie 15 wprowadziliśmy zmiany w działaniu, które mogą mieć wpływ na Twoją aplikację. Poniższe zmiany w działaniu dotyczą wyłącznie aplikacji kierowanych na Androida 15 lub nowszego. Jeśli Twoja aplikacja jest kierowana na Androida 15 lub nowszego, zmodyfikuj ją, aby w odpowiednich przypadkach obsługiwała te działania.

Zapoznaj się też z listą zmian w działaniu, które wpływają na wszystkie aplikacje działające na Androidzie 15 niezależnie od targetSdkVersion aplikacji.

Główna funkcja

Android 15 modyfikuje lub rozszerza różne podstawowe funkcje systemu Android.

Zmiany w usługach działających na pierwszym planie

W Androidzie 15 wprowadzamy następujące zmiany w usługach działających na pierwszym planie.

Zachowanie limitu czasu usługi na pierwszym planie synchronizującej dane

对于以 Android 15(API 级别 35)或更高版本为目标平台的应用,Android 15 为 dataSync 引入了新的超时行为。此行为也适用于新的 mediaProcessing 前台服务类型。

系统允许应用的 dataSync 服务在 24 小时内总共运行 6 小时,之后系统会调用正在运行的服务的 Service.onTimeout(int, int) 方法(在 Android 15 中引入)。此时,该服务有几秒钟时间来调用 Service.stopSelf()。调用 Service.onTimeout() 后,该服务将不再被视为前台服务。如果服务未调用 Service.stopSelf(),系统会抛出内部异常。系统会在 Logcat 中记录此异常,并显示以下消息:

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

为避免因行为变更而导致问题,您可以执行以下一项或多项操作:

  1. 让您的服务实现新的 Service.onTimeout(int, int) 方法。当您的应用收到回调时,请务必在几秒钟内调用 stopSelf()。(如果您不立即停止应用,系统会生成故障。)
  2. 确保应用的 dataSync 服务在任何 24 小时内总运行时间不超过 6 小时(除非用户与应用互动,重置计时器)。
  3. 仅通过直接的用户互动来启动 dataSync 前台服务;由于您的应用在服务启动时位于前台,因此服务会在应用进入后台后的 6 小时内完整运行。
  4. 请改用替代 API,而不是使用 dataSync 前台服务。

如果您的应用的 dataSync 前台服务在过去 24 小时内运行了 6 小时,则您无法启动其他 dataSync 前台服务,除非用户已将您的应用切换到前台(这会重置计时器)。如果您尝试启动其他 dataSync 前台服务,系统会抛出 ForegroundServiceStartNotAllowedException,并显示类似“前台服务类型 dataSync 的时间限制已用尽”的错误消息。

测试

如需测试应用的行为,您可以启用数据同步超时功能,即使应用未以 Android 15 为目标平台也是如此(前提是应用在 Android 15 设备上运行)。如需启用超时,请运行以下 adb 命令:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

您还可以调整超时期限,更轻松地测试应用在达到此限制时的行为。如需设置新的超时期限,请运行以下 adb 命令:

adb shell device_config put activity_manager data_sync_fgs_timeout_duration duration-in-milliseconds

Nowy typ usługi działającej na pierwszym planie do przetwarzania multimediów

W Androidzie 15 wprowadziliśmy nowy typ usługi na pierwszym planie: mediaProcessing. Ten typ usługi jest odpowiedni do operacji takich jak transkodowanie plików multimedialnych. Na przykład aplikacja multimedialna może pobrać plik audio i przed odtworzeniem musi przekonwertować go na inny format. Możesz użyć usługi mediaProcessing na pierwszym planie, aby mieć pewność, że konwersja będzie kontynuowana nawet wtedy, gdy aplikacja będzie działać w tle.

System zezwala na działanie usług mediaProcessing aplikacji przez łącznie 6 godzin w okresie 24 godzin. Po tym czasie system wywołuje metodę Service.onTimeout(int, int) (wprowadzoną w Androidzie 15). Obecnie usługa ma kilka sekund na wywołanie funkcji Service.stopSelf(). Jeśli usługa nie wywołuje funkcji Service.stopSelf(), system zgłasza wewnętrzny wyjątek. Wyjątek jest logowany w Logcat z tym komunikatem:

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

Aby uniknąć wyjątku, wykonaj jedną z tych czynności:

  1. Zaimplementuj nową metodę Service.onTimeout(int, int) w swojej usłudze. Gdy aplikacja otrzyma połączenie zwrotne, w ciągu kilku sekund zadzwoń pod numer stopSelf(). (jeśli nie zatrzymasz aplikacji od razu, system wygeneruje błąd).
  2. Upewnij się, że usługi mediaProcessing w aplikacji nie działają dłużej niż 6 godzin w ciągu 24 godzin (chyba że użytkownik wchodzi w interakcję z aplikacją, zresetowując w ten sposób timer).
  3. Uruchamiaj usługi na pierwszym planie mediaProcessing tylko w wyniku bezpośredniej interakcji z użytkownikiem. Ponieważ aplikacja jest na pierwszym planie, gdy usługa się uruchamia, ma ona 6 godzin na działanie po przejściu aplikacji w tło.
  4. Zamiast usługi na pierwszym planie mediaProcessing użyj alternatywnego interfejsu API, takiego jak WorkManager.

Jeśli w ciągu ostatnich 24 godzin usługi mediaProcessing na pierwszym planie aplikacji działały przez 6 godzin, nie możesz uruchomić innej usługi mediaProcessing na pierwszym planie chyba że użytkownik przełączył aplikację na pierwszy plan (co spowoduje zresetowanie minutnika). Jeśli spróbujesz uruchomić inną usługę działającą na pierwszym planie mediaProcessing, system wyświetli komunikat o błędzie podobny do „Czas limitu usługi działającej na pierwszym planie typu mediaProcessing został już wykorzystany”.ForegroundServiceStartNotAllowedException

Więcej informacji o typie usługi mediaProcessing znajdziesz w artykule Zmiany w typach usług na pierwszym planie w Androidzie 15: przetwarzanie multimediów.

Testowanie

Aby przetestować działanie aplikacji, możesz włączyć limity czasu przetwarzania multimediów, nawet jeśli aplikacja nie jest kierowana na Androida 15 (o ile jest uruchomiona na urządzeniu z Androidem 15). Aby włączyć limity czasu, uruchom to polecenie adb:

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

Możesz też dostosować czas oczekiwania, aby łatwiej testować działanie aplikacji po osiągnięciu limitu. Aby ustawić nowy okres oczekiwania, uruchom to polecenie adb:

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

Ograniczenia dotyczące odbiorników BOOT_COMPLETED uruchamiających usługi na pierwszym planie

Wprowadziliśmy nowe ograniczenia dotyczące odbiorników BOOT_COMPLETED usług działających na pierwszym planie. Odbiorcy BOOT_COMPLETED nie mogą uruchamiać modułu te typy usług na pierwszym planie:

Jeśli odbiornik BOOT_COMPLETED próbuje uruchomić którykolwiek z tych typów działania na pierwszym planie usług, system wywołuje ForegroundServiceStartNotAllowedException.

Testowanie

Aby przetestować działanie aplikacji, możesz włączyć te nowe ograniczenia, nawet jeśli aplikacja nie jest kierowana na Androida 15 (o ile jest ona uruchomiona na urządzeniu z Androidem 15). Uruchom to polecenie adb:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

Aby wysłać komunikat typu BOOT_COMPLETED bez ponownego uruchamiania urządzenia: uruchom to polecenie adb:

adb shell am broadcast -a android.intent.action.BOOT_COMPLETED your-package-name

Ograniczenia dotyczące uruchamiania usług na pierwszym planie, gdy aplikacja ma uprawnienia SYSTEM_ALERT_WINDOW

Wcześniej, jeśli aplikacja miała uprawnienia SYSTEM_ALERT_WINDOW, mogła uruchomić usługę na pierwszym planie, nawet jeśli była w tej chwili uruchomiona w tle (jak opisano w wyjątkach od ograniczeń dotyczących uruchamiania w tle).

Jeśli aplikacja jest kierowana na Androida 15, wykluczenie jest teraz bardziej precyzyjne. Aplikacja musi mieć teraz uprawnienia SYSTEM_ALERT_WINDOW, a także mieć widoczne okno nakładki. Oznacza to, że aplikacja musi najpierw uruchomić okno TYPE_APPLICATION_OVERLAY i to okno musi być widoczne, zanim uruchomisz usługę na pierwszym planie.

Jeśli aplikacja próbuje uruchomić usługę działającą na pierwszym planie w tle bez spełnienia tych nowych wymagań (i nie ma żadnych innych wyjątków), system zgłasza ForegroundServiceStartNotAllowedException.

Jeśli Twoja aplikacja deklaruje uprawnienie SYSTEM_ALERT_WINDOW i uruchamia usługi na pierwszym planie z poziomu usług działających w tle, może być dotknięta tą zmianą. Jeśli Twoja aplikacja otrzymuje ForegroundServiceStartNotAllowedException, sprawdź kolejność jej działań i upewnij się, że aplikacja ma już aktywne okno nakładki, zanim spróbuje uruchomić usługę na pierwszym planie z tle. Aby sprawdzić, czy okno nakładki jest obecnie widoczne, wywołaj View.getWindowVisibility() lub zastąp View.onWindowVisibilityChanged(), aby otrzymywać powiadomienia o zmianie widoczności.

Testowanie

Aby przetestować działanie aplikacji, możesz włączyć te nowe ograniczenia, nawet jeśli aplikacja nie jest kierowana na Androida 15 (o ile jest ona uruchomiona na urządzeniu z Androidem 15). Aby włączyć nowe ograniczenia dotyczące uruchamiania usług na pierwszym planie w tle, uruchom to polecenie adb:

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

Zmiany dotyczące tego, kiedy aplikacje mogą modyfikować globalny stan trybu Nie przeszkadzać

以 Android 15(API 级别 35)及更高版本为目标平台的应用无法再更改设备上的勿扰 (DND) 功能的全局状态或政策(无论是通过修改用户设置还是关闭勿扰模式)。相反,应用必须提供 AutomaticZenRule,系统会将其与现有的“最严格的政策优先”方案合并为一个全局政策。对之前会影响全局状态的现有 API 的调用(setInterruptionFilter、setNotificationPolicy)会导致创建或更新隐式 AutomaticZenRule,该 AutomaticZenRule 会根据这些 API 调用的调用周期开启和关闭。

请注意,只有当应用调用 setInterruptionFilter(INTERRUPTION_FILTER_ALL) 并希望该调用停用之前由其所有者激活的 AutomaticZenRule 时,此更改才会影响可观察到的行为。

Zmiany w interfejsie OpenJDK API

Android 15 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致。

其中一些变更可能会影响以 Android 15(API 级别 35)为目标平台的应用兼容性:

  • 对字符串格式化 API 进行了更改:现在,在使用以下 String.format() 和 Formatter.format() API 时,对参数索引、标志、宽度和精度的验证要求变得更加严格:

    例如,如果使用参数索引 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() 匹配。

当您在应用的 build 配置中将 compileSdk 更新为使用 Android 15(API 级别 35)后,新的 SequencedCollection API 可能会影响应用的兼容性:

  • 与 kotlin-stdlib 中的 MutableList.removeFirst() 和 MutableList.removeLast() 扩展函数冲突

    Java 中的 List 类型会映射到 Kotlin 中的 MutableList 类型。 由于 List.removeFirst() 和 List.removeLast() API 已在 Android 15(API 级别 35)中引入,因此 Kotlin 编译器会将函数调用(例如 list.removeFirst())静态解析为新的 List API,而不是 kotlin-stdlib 中的扩展函数。

    如果应用在重新编译时将 compileSdk 设为 35,将 minSdk 设为 34 或更低,然后在 Android 14 及更低版本上运行,则会抛出运行时错误:

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

    Android Gradle 插件中的现有 NewApi lint 选项可以捕获这些新的 API 用法。

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

    如需修复运行时异常和 lint 错误,可以在 Kotlin 中分别将 removeFirst() 和 removeLast() 函数调用替换为 removeAt(0) 和 removeAt(list.lastIndex)。如果您使用的是 Android Studio Ladybug | 2024.1.3 或更高版本,它还提供了针对这些错误的快速修复选项。

    如果 lint 选项已被停用,请考虑移除 @SuppressLint("NewApi") 和 lintOptions { disable 'NewApi' }。

  • 与 Java 中的其他方法冲突

    现有类型中添加了新方法,例如 List 和 Deque。这些新方法可能与其他接口和类中具有相同名称和参数类型的方法不兼容。如果方法签名冲突且不兼容,javac 编译器会输出构建时错误。例如:

    错误示例 1:

    javac MyList.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();
    }
    

Bezpieczeństwo

Android 15 zawiera zmiany, które zwiększają bezpieczeństwo systemu, aby chronić aplikacje i użytkowników przed szkodliwymi aplikacjami.

Ograniczone wersje TLS

Android 15 限制了对 TLS 版本 1.0 和 1.1 的使用。这些版本之前已在 Android 中被弃用,但现在不允许面向 Android 15 的应用使用。

Bezpieczne uruchamianie aktywności w tle

Android 15 chroni użytkowników przed złośliwymi aplikacjami i zapewnia im większą kontrolę nad urządzeniami. Wprowadziliśmy zmiany, które uniemożliwiają złośliwym aplikacjom działającym w tle przenoszenie innych aplikacji na pierwszy plan, podnoszenie ich uprawnień i nadużywanie interakcji z użytkownikiem. Od Androida 10 (poziom interfejsu API 29) uruchamianie aktywności w tle jest ograniczone.

Inne zmiany

  • Zmień domyślne ustawienie PendingIntent twórców na blokowanie uruchamiania aktywności w tle. Pomaga to zapobiegać przypadkowemu tworzeniu przez aplikacjePendingIntent, które mogłyby być wykorzystywane przez nieuczciwe podmioty.
  • Nie przenoś aplikacji na pierwszy plan, chyba że PendingIntentnadawca na to zezwoli. Ta zmiana ma zapobiegać nadużywaniu przez złośliwe aplikacje możliwości rozpoczynania aktywności w tle. Domyślnie aplikacje nie mogą przenosić stosu zadań na pierwszy plan, chyba że twórca zezwoli na uprawnienia do uruchamiania działań w tle lub nadawca ma uprawnienia do uruchamiania działań w tle.
  • Określa, jak najwyższa aktywność w stosie zadań może zakończyć zadanie. Jeśli aktywność na górze stosu zakończy zadanie, Android wróci do ostatniego aktywnego zadania. Jeśli aktywność niebędąca na pierwszym planie zakończy swoje zadanie, Android wró do ekranu głównego. Nie zablokuje zakończenia tej aktywności.
  • Zapobiegaj uruchamianiu dowolnych działań z innych aplikacji w swoim zadaniu. Ta zmiana uniemożliwia złośliwym aplikacjom wyłudzanie informacji od użytkowników przez tworzenie działań, które wyglądają jak działania innych aplikacji.
  • Blokowanie okien niewidocznych, aby nie były brane pod uwagę podczas uruchamiania aktywności w tle. Pomaga to zapobiegać wykorzystywaniu przez złośliwe aplikacje uruchamiania aktywności w tle do wyświetlania użytkownikom niechcianych lub szkodliwych treści.

Bezpieczniejsze intencje

W Androidzie 15 wprowadzono StrictMode w przypadku intencji.

Aby wyświetlić szczegółowe dzienniki naruszeń zasad korzystania z Intent, wykonaj te czynności:

Kotlin

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

Java

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

Interfejs użytkownika i systemowy

Android 15 zawiera pewne zmiany, które mają na celu zapewnienie bardziej spójnego i intuicyjnego działania.

Zmiany wstawki okna

W Androidzie 15 wprowadzono 2 zmiany związane z współrzędnymi okna: domyślnie wymuszono tryb pełnoekranowy, a także wprowadzono zmiany konfiguracji, takie jak domyślna konfiguracja pasków systemowych.

全面实施政策

Aplikacje są domyślnie wyświetlane bez ramki na urządzeniach z Androidem 15, jeśli są kierowane na Androida 15 (poziom API 35).

Aplikacja kierowana na Androida 14, która nie wyświetla się bez ramki na urządzeniu z Androidem 15.


Aplikacja kierowana na Androida 15 (poziom API 35) i wyświetlana od krawędzi do krawędzi na urządzeniu z Androidem 15. Ta aplikacja używa głównie komponentów Material 3 Compose, które automatycznie stosują wstawki. Wymóg wyświetlania od krawędzi do krawędzi na Androidzie 15 nie ma negatywnego wpływu na ten ekran.

Jest to zmiana powodująca niezgodność, która może negatywnie wpłynąć na interfejs aplikacji. Zmiany dotyczą tych obszarów interfejsu:

  • Pasek nawigacyjny z uchwytem gestu
    • Domyślnie przezroczysty.
    • Przesunięcie dolne jest wyłączone, więc treść jest wyświetlana za paskiem nawigacyjnym systemu, chyba że zastosowano wstawki.
    • Atrybuty setNavigationBarColor i R.attr#navigationBarColor są wycofane i nie mają wpływu na nawigację przy użyciu gestów.
    • setNavigationBarContrastEnforced i R.attr#navigationBarContrastEnforced nadal nie mają wpływu na nawigację przy użyciu gestów.
  • Nawigacja przy użyciu 3 przycisków
    • Domyślnie nieprzezroczystość jest ustawiona na 80%, a kolor może pasować do tła okna.
    • Przesunięcie dolne jest wyłączone, więc treści są rysowane za paskiem nawigacyjnym systemu, chyba że zastosowano wstawki.
    • Domyślnie setNavigationBarColor i R.attr#navigationBarColor są ustawione tak, aby pasowały do tła okna. Aby można było zastosować tę wartość domyślną, tło okna musi być obiektem rysowalnym koloru. Ten interfejs API został wycofany, ale nadal wpływa na nawigację przy użyciu 3 przycisków.
    • setNavigationBarContrastEnforced i R.attr#navigationBarContrastEnforced są domyślnie ustawione na wartość „true”, co powoduje dodanie tła o kryciu 80% w nawigacji z 3 przyciskami.
  • Pasek stanu
    • Domyślnie przezroczysty.
    • Przesunięcie od góry jest wyłączone, więc treści są wyświetlane za paskiem stanu, chyba że zastosowano wstawki.
    • setStatusBarColor i R.attr#statusBarColor są wycofane i nie mają wpływu na Androida 15.
    • Atrybuty setStatusBarContrastEnforced i R.attr#statusBarContrastEnforced zostały wycofane, ale nadal działają na Androidzie 15.
  • Wycięcie w ekranie
    • layoutInDisplayCutoutMode niepływających okien musi być LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. SHORT_EDGES, NEVER i DEFAULT są interpretowane jako ALWAYS, dzięki czemu użytkownicy nie widzą czarnego paska spowodowanego wycięciem w ekranie, a obraz jest wyświetlany od krawędzi do krawędzi.

Poniższy przykład pokazuje aplikację przed kierowaniem na Androida 15 (API na poziomie 35) i po nim oraz przed zastosowaniem odcięć i po nim. Ten przykład nie jest wyczerpujący i może wyglądać inaczej w Androidzie Auto.

Aplikacja kierowana na Androida 14, która nie wyświetla się bez ramki na urządzeniu z Androidem 15.
Aplikacja kierowana na Androida 15 (API na poziomie 35) i wyświetlana od krawędzi do krawędzi na urządzeniu z Androidem 15. Jednak wiele elementów jest teraz ukrytych przez pasek stanu, pasek nawigacyjny z 3 przyciskami lub wycięcie w ekranie z powodu wymuszenia wyświetlania bez ramki w Androidzie 15. Ukryty interfejs obejmuje górny pasek aplikacji Material 2, pływające przyciski polecenia i elementy listy.
Aplikacja kierowana na Androida 15 (API na poziomie 35) jest wyświetlana na urządzeniu z Androidem 15 od krawędzi do krawędzi i stosuje wstawki, dzięki czemu interfejs nie jest ukryty.
Co sprawdzić, jeśli aplikacja jest już wyświetlana od krawędzi do krawędzi

Jeśli Twoja aplikacja jest już od krawędzi do krawędzi i stosuje wstawki, nie będzie to miało na nią wpływu, z wyjątkiem tych sytuacji: Nawet jeśli uważasz, że nie dotyczy to Twojej aplikacji, zalecamy jej przetestowanie.

  • Masz okno, które nie jest pływające, np. Activity, które używa SHORT_EDGES, NEVER lub DEFAULT zamiast LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Jeśli aplikacja ulega awarii przy uruchamianiu, może to być spowodowane ekranem powitalnym. Możesz uaktualnić zależność core splashscreen do wersji 1.2.0-alpha01 lub nowszej albo ustawić window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutInDisplayCutoutMode.always.
  • Mogą występować ekrany o mniejszym natężeniu ruchu z zasłoniętym interfejsem. Sprawdź, czy te rzadziej odwiedzane ekrany nie mają zasłoniętego interfejsu. Ekrany o mniejszym natężeniu ruchu to m.in.:
    • ekrany wprowadzenia lub logowania,
    • Strony ustawień
Co sprawdzić, jeśli aplikacja nie jest jeszcze wyświetlana od krawędzi do krawędzi

Jeśli Twoja aplikacja nie jest jeszcze wyświetlana od krawędzi do krawędzi, prawdopodobnie dotyczy to również Ciebie. Oprócz scenariuszy dotyczących aplikacji, które są już wyświetlane od krawędzi do krawędzi, weź pod uwagę te kwestie:

  • Jeśli Twoja aplikacja używa komponentów Material 3 (androidx.compose.material3) w Compose, takich jak TopAppBar, BottomAppBar i NavigationBar, prawdopodobnie nie będzie to miało na nie wpływu, ponieważ automatycznie obsługują one wstawki.
  • Jeśli aplikacja używa komponentów Material 2 (androidx.compose.material) w Compose, te komponenty nie obsługują automatycznie wcięć. Możesz jednak uzyskać dostęp do wstawek i zastosować je ręcznie. W androidx.compose.material 1.6.0 i nowszych wersjach użyj parametru windowInsets, aby ręcznie zastosować dopełnienia w przypadku BottomAppBar, TopAppBar, BottomNavigation i NavigationRail. Podobnie użyj parametru contentWindowInsets dla Scaffold.
  • Jeśli Twoja aplikacja używa widoków i komponentów Material Design (com.google.android.material), większość komponentów Material Design opartych na widokach, takich jak BottomNavigationView, BottomAppBar, NavigationRailView czy NavigationView, obsługuje wstawki i nie wymaga dodatkowych działań. Jeśli jednak używasz AppBarLayout, musisz dodać android:fitsSystemWindows="true".
  • W przypadku niestandardowych funkcji kompozycyjnych zastosuj wstawki ręcznie jako dopełnienie. Jeśli treści znajdują się w komponencie Scaffold, możesz wykorzystać wstawki, używając wartości dopełnienia Scaffold. W przeciwnym razie zastosuj dopełnienie, używając jednego z tych elementów: WindowInsets.
  • Jeśli aplikacja używa widoków i kontenerów BottomSheet, SideSheet lub niestandardowych, zastosuj dopełnienie za pomocą ViewCompat.setOnApplyWindowInsetsListener. W przypadku RecyclerView zastosuj dopełnienie za pomocą tego detektora i dodaj też clipToPadding="false".
Co sprawdzić, jeśli aplikacja musi oferować niestandardową ochronę w tle

Jeśli aplikacja musi oferować niestandardową ochronę tła w przypadku nawigacji z 3 przyciskami lub paska stanu, powinna umieścić element kompozycyjny lub widok za paskiem systemowym, używając WindowInsets.Type#tappableElement(), aby uzyskać wysokość paska nawigacyjnego z 3 przyciskami, lub WindowInsets.Type#statusBars.

Dodatkowe materiały dotyczące wyświetlania od krawędzi do krawędzi

Dodatkowe informacje o stosowaniu wcięć znajdziesz w przewodnikach Widoki od krawędzi do krawędzi i Tworzenie od krawędzi do krawędzi.

Wycofane interfejsy API

Te interfejsy API zostały wycofane, ale nie zostały wyłączone:

Te interfejsy API zostały wycofane i wyłączone:

稳定配置

Jeśli aplikacja jest kierowana na Androida 15 (API na poziomie 35) lub nowszego, Configuration nie wyklucza już pasków systemowych. Jeśli do obliczania układu używasz rozmiaru ekranu w klasie Configuration, zastąp go lepszymi alternatywami, takimi jak odpowiedni ViewGroup, WindowInsets lub WindowMetricsCalculator, w zależności od potrzeb.

Configuration jest dostępny od wersji 1 interfejsu API. Zwykle można go uzyskać od Activity.onConfigurationChanged. Dostarcza informacji takich jak gęstość, orientacja i rozmiary okna. Jedną z ważnych cech rozmiarów okien zwracanych przez Configuration jest to, że wcześniej nie uwzględniały one pasków systemowych.

Rozmiar konfiguracji jest zwykle używany do wyboru zasobów, takich jak /res/layout-h500dp, i nadal jest to prawidłowy przypadek użycia. Jednak używanie go do obliczania układu zawsze było odradzane. Jeśli tak, odsuń się od niego. Zastąp Configuration czymś bardziej odpowiednim w zależności od przypadku użycia.

Jeśli używasz go do obliczania układu, użyj odpowiedniego elementu ViewGroup, takiego jak CoordinatorLayout lub ConstraintLayout. Jeśli używasz go do określania wysokości systemowego paska nawigacyjnego, użyj WindowInsets. Jeśli chcesz poznać bieżący rozmiar okna aplikacji, użyj skrótu computeCurrentWindowMetrics.

Poniższa lista zawiera pola, których dotyczy ta zmiana:

Atrybut elegantTextHeight ma domyślnie wartość „true”

W przypadku aplikacji przeznaczonych na Androida 15 (poziom interfejsu API 35) atrybut elegantTextHeight TextView domyślnie staje się true, co zastępuje czcionkę kompaktową używaną domyślnie w niektórych skryptach, które mają duże wymiary pionowe, czcionką o znacznie lepszej czytelności. Czcionka kompaktowa została wprowadzona, aby zapobiec rozmieszczaniu elementów układu. Android 13 (poziom interfejsu API 33) zapobiega wielu takim problemom, ponieważ pozwala na rozciąganie wysokości układu tekstu za pomocą atrybutu fallbackLineSpacing.

W Androidzie 15 czcionka kompaktowa nadal pozostaje w systemie, więc aplikacja może ustawić wartość elegantTextHeight na false, aby uzyskać takie samo działanie jak wcześniej, ale jest mało prawdopodobne, aby była obsługiwana w przyszłych wersjach. Jeśli Twoja aplikacja obsługuje te pismo: arabski, kannada, oriya, tamilski, telugu, gudżarati, malajalam, birmański, gudźarati, kannada, malajalam, oriya, telugu lub tajski, przetestuj ją, ustawiając wartość elegantTextHeight na true.

elegantTextHeightdla aplikacji kierowanych na Androida 14 (poziom API 34) i starszego.
elegantTextHeightzachowanie w przypadku aplikacji kierowanych na Androida 15.

Zmiany szerokości TextView w przypadku złożonych kształtów liter

W poprzednich wersjach Androida niektóre czcionki kursywe lub języki ze złożonym kształtem mogą rysować litery w obszarze poprzedniego lub następnego znaku. Zdarzało się, że takie litery były obcinane na początku lub na końcu. Od Androida 15 TextView przydziela wystarczającą ilość miejsca na wyświetlenie takich liter, a aplikacje mogą prosić o dodatkowe wypełnienie po lewej stronie, aby zapobiec przycięciu.

Ta zmiana wpływa na sposób określania szerokości przez TextView, więc TextView domyślnie przydziela więcej szerokości, jeśli aplikacja jest kierowana na Androida 15 (poziom API 35) lub nowszego. Możesz włączyć lub wyłączyć tę funkcję, wywołując interfejs API setUseBoundsForWidth w komponencie TextView.

Dodanie dopełnienia z lewej strony może spowodować niewłaściwe ułożenie istniejących układów, dlatego dopełnienie nie jest dodawane domyślnie nawet w przypadku aplikacji kierowanych na Androida 15 lub nowszego. Możesz jednak dodać dodatkowy margines, aby zapobiec przycięciu, wywołując funkcję setShiftDrawingOffsetForStartOverhang.

W poniższych przykładach pokazujemy, jak te zmiany mogą poprawić układ tekstu w przypadku niektórych czcionek i języków.

Standardowy układ angielskiej czcionki zapisanej kursywą. Niektóre litery są przycięte. Oto odpowiedni kod XML:

<TextView
    android:fontFamily="cursive"
    android:text="java" />
Układ tego samego tekstu w języku angielskim z dodatkową szerokością i odstępem. Oto odpowiedni kod XML:

<TextView
    android:fontFamily="cursive"
    android:text="java"
    android:useBoundsForWidth="true"
    android:shiftDrawingOffsetForStartOverhang="true" />
Standardowy układ tekstu tajskiego. Niektóre litery są przycięte. Oto odpowiedni kod XML:

<TextView
    android:text="คอมพิวเตอร์" />
Układ tego samego tekstu w języku tajskim z dodatkową szerokością i dopełnieniem. Oto odpowiedni kod XML:

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

Domyślna wysokość wiersza w polu EditText zależna od ustawień regionalnych

W poprzednich wersjach Androida układ tekstu rozciągał wysokość tekstu, aby dopasować ją do wysokości wiersza czcionki odpowiadającej bieżącemu ustawieniu języka. Jeśli na przykład treści były w języku japońskim, to ze względu na to, że wysokość linii czcionki japońskiej jest nieco większa niż czcionki łacińskiej, wysokość tekstu była nieco większa. Jednak pomimo tych różnic w wysokościach wierszy element EditText miał jednakowy rozmiar niezależnie od używanej lokalizacji, jak pokazano na poniższym obrazku:

Trzy pola reprezentujące elementy EditText, które mogą zawierać tekst w języku angielskim (en), japońskim (ja) i birmańskim (my). Wysokość EditText jest taka sama, mimo że te języki mają różne wysokości linii.

W przypadku aplikacji kierowanych na Androida 15 (poziom interfejsu API 35) minimalna wysokość linii jest teraz zarezerwowana dla EditText, aby pasowała do czcionki referencyjnej dla określonego języka, jak pokazano na poniższym obrazie:

Trzy pola reprezentujące elementy EditText, które mogą zawierać tekst w języku angielskim (en), japońskim (ja) i birmańskim (my). Wysokość EditText uwzględnia teraz miejsce na domyślną wysokość wiersza dla czcionek w tych językach.

W razie potrzeby aplikacja może przywrócić poprzednie działanie, ustawiając atrybut useLocalePreferredLineHeightForMinimum na false. Może też ustawić niestandardowe minimalne dane pionowe za pomocą interfejsu API setMinimumFontMetrics w językach Kotlin i Java.

Aparat i multimedia

W Androidzie 15 wprowadziliśmy te zmiany w działaniu aparatu i multimediów w przypadku aplikacji kierowanych na Androida 15 lub nowszego.

Ograniczenia dotyczące żądania fokusu audio

以 Android 15(API 级别 35)为目标平台的应用必须是顶部应用或正在运行前台服务,才能请求音频焦点。如果应用在未满足上述任一要求的情况下尝试请求焦点,调用将返回 AUDIOFOCUS_REQUEST_FAILED。

如需详细了解音频焦点,请参阅管理音频焦点。

Zaktualizowane ograniczenia dotyczące interfejsów niebędących częścią pakietu SDK

Android 15 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。

如果您的应用并非以 Android 15 为目标平台,其中一些变更可能不会立即对您产生影响。不过,虽然您的应用可以访问某些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,就很有可能会造成应用功能异常。

如果您不确定自己的应用是否使用了非 SDK 接口,则可以通过测试该应用来进行确认。如果您的应用依赖非 SDK 接口,则应开始计划迁移到 SDK 替代方案。不过,我们知道某些应用存在使用非 SDK 接口的合理场景。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,则应该请求新的公共 API。

Więcej informacji o zmianach w tej wersji Androida znajdziesz w artykule Zmiany ograniczeń interfejsu niebędącego interfejsem SDK w Androidzie 15. Więcej informacji o interfejsach innych niż SDK znajdziesz w artykule Ograniczenia interfejsów innych niż SDK.