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 wprowadza nowe zachowanie dotyczące limitu czasu w przypadku dataSync w aplikacjach kierowanych na Androida 15 (poziom interfejsu API 35) lub nowszego. Takie zachowanie dotyczy też nowego mediaProcessingtypu usługi działającej na pierwszym planie.

System zezwala na działanie usług dataSync aplikacji przez łącznie 6 godzin w ciągu 24 godzin, po czym wywołuje metodę Service.onTimeout(int, int) działającej usługi (wprowadzoną w Androidzie 15). W tej chwili usługa ma kilka sekund na wywołanie metody Service.stopSelf(). Gdy wywołana zostanie usługa Service.onTimeout(), nie będzie ona już usługą na pierwszym planie. Jeśli usługa nie wywołuje funkcji Service.stopSelf(), system zgłasza wewnętrzny wyjątek. Wyjątek jest rejestrowany w Logcat z tym komunikatem:

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

Aby uniknąć problemów związanych z tą zmianą zachowania, możesz wykonać co najmniej 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 dataSync Twojej aplikacji nie działają przez łącznie 6 godzin w dowolnym okresie 24 godzin (chyba że użytkownik wejdzie w interakcję z aplikacją, resetując minutnik).
  3. Uruchamiaj dataSync usługi na pierwszym planie wyłącznie w wyniku bezpośredniej interakcji z użytkownikiem. Ponieważ aplikacja znajduje się na pierwszym planie w momencie uruchomienia usługi, usługa ma pełne 6 godzin od uruchomienia w tle.
  4. Zamiast usługi na pierwszym planie dataSync użyj alternatywnego interfejsu API.

Jeśli w ciągu ostatnich 24 godzin usługi dataSync na pierwszym planie Twojej aplikacji działały przez 6 godzin, nie możesz uruchomić innej usługi dataSync 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ę na pierwszym planie dataSync, system wygeneruje ForegroundServiceStartNotAllowedException z komunikatem o błędzie, np. „Limit czasu dla usługi działającej na pierwszym planie typu dataSync został już wyczerpany”.

Testowanie

Aby przetestować działanie aplikacji, możesz włączyć czas oczekiwania na synchronizację danych, nawet jeśli aplikacja nie jest kierowana na Androida 15 (o ile działa 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ć limit czasu 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 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

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

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

Aplikacje kierowane na Androida 15 (poziom API 35) lub nowszego nie mogą już zmieniać globalnego stanu ani zasad trybu Nie przeszkadzać na urządzeniu (ani przez modyfikowanie ustawień użytkownika, ani przez wyłączanie trybu Nie przeszkadzać). Zamiast tego aplikacje muszą przekazać AutomaticZenRule, które system połączy w globalne zasady z dotychczasowym schematem „najbardziej restrykcyjne zasady wygrywają”. Wywołania istniejących interfejsów API, które wcześniej wpływały na stan globalny (setInterruptionFilter, setNotificationPolicy), powodują utworzenie lub zaktualizowanie niejawnego AutomaticZenRule, który jest włączany i wyłączany w zależności od cyklu wywołań tych interfejsów API.

Pamiętaj, że ta zmiana wpływa tylko na obserwowalne zachowanie, jeśli aplikacja wywołuje funkcję setInterruptionFilter(INTERRUPTION_FILTER_ALL) i oczekuje, że ta funkcja dezaktywuje AutomaticZenRule, który został wcześniej aktywowany przez właścicieli.

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 ogranicza użycie TLS w wersjach 1.0 i 1.1. Te wersje zostały wcześniej wycofane w Androidzie, ale teraz są niedozwolone w przypadku aplikacji kierowanych na Androida 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

Android 15 针对 intent 引入了 StrictMode。

如需查看有关 Intent 使用违规行为的详细日志,请使用以下方法:

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

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

Egzekwowanie treści od krawędzi do krawędzi

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:

Stabilna konfiguracja

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:

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

Aplikacje kierowane na Androida 15 (poziom API 35) muszą być aplikacjami na pierwszym planie lub usługami na pierwszym planie, aby poprosić o uzyskanie kontroli nad dźwiękiem. Jeśli aplikacja próbuje poprosić o skupienie uwagi, gdy nie spełnia jednego z tych wymagań, wywołanie zwraca AUDIOFOCUS_REQUEST_FAILED.

Więcej informacji o fokusowaniu dźwięku znajdziesz w artykule Zarządzanie fokusem dźwięku.

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

Android 15 zawiera zaktualizowane listy ograniczonych interfejsów spoza pakietu SDK, które powstały we współpracy z programistami aplikacji na Androida i na podstawie najnowszych testów wewnętrznych. Zanim ograniczymy dostęp do interfejsów innych niż SDK, staramy się zapewnić publiczne alternatywy.

Jeśli Twoja aplikacja nie jest kierowana na Androida 15, niektóre z tych zmian mogą nie mieć na nią natychmiastowego wpływu. Chociaż Twoja aplikacja może mieć dostęp do niektórych interfejsów spoza pakietu SDK w zależności od docelowego poziomu interfejsu API aplikacji, używanie jakiejkolwiek metody lub pola spoza pakietu SDK zawsze wiąże się z dużym ryzykiem uszkodzenia aplikacji.

Jeśli nie masz pewności, czy Twoja aplikacja używa interfejsów innych niż SDK, możesz to sprawdzić, testując aplikację. Jeśli Twoja aplikacja korzysta z interfejsów innych niż SDK, zacznij planować migrację do alternatywnych rozwiązań SDK. Zdajemy sobie jednak sprawę, że w przypadku niektórych aplikacji używanie interfejsów spoza pakietu SDK jest uzasadnione. Jeśli nie możesz znaleźć alternatywy dla interfejsu spoza pakietu SDK w przypadku funkcji w aplikacji, poproś o nowy publiczny interfejs API.

如需详细了解此 Android 版本中的变更,请参阅 Android 15 中有关限制非 SDK 接口的更新。如需全面了解有关非 SDK 接口的详细信息,请参阅对非 SDK 接口的限制。