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

我们将对 Android 15 中的前台服务进行以下更改。

数据同步前台服务超时行为

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

新的媒体处理前台服务类型

Android 15 introduces a new foreground service type, mediaProcessing. This service type is appropriate for operations like transcoding media files. For example, a media app might download an audio file and need to convert it to a different format before playing it. You can use a mediaProcessing foreground service to make sure the conversion continues even while the app is in the background.

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

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

To avoid having the exception, you can do one of the following:

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

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

For more information about the mediaProcessing service type, see Changes to foreground service types for Android 15: Media processing.

Testing

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

adb shell am compat enable FGS_INTRODUCE_TIME_LIMITS your-package-name

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

adb shell device_config put activity_manager media_processing_fgs_timeout_duration duration-in-milliseconds

对启动前台服务的 BOOT_COMPLETED 广播接收器的限制

There are new restrictions on BOOT_COMPLETED broadcast receivers launching foreground services. BOOT_COMPLETED receivers are not allowed to launch the following types of foreground services:

If a BOOT_COMPLETED receiver tries to launch any of those types of foreground services, the system throws ForegroundServiceStartNotAllowedException.

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). Run the following adb command:

adb shell am compat enable FGS_BOOT_COMPLETED_RESTRICTIONS your-package-name

To send a BOOT_COMPLETED broadcast without restarting the device, run the following adb command:

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

在应用拥有 SYSTEM_ALERT_WINDOW 权限时启动前台服务的限制

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

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

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

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

Testing

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

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

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

W Androidzie 15 kontynuujemy odświeżanie podstawowych bibliotek Androida, aby dostosować je do funkcji najnowszych wersji OpenJDK LTS.

Niektóre z tych zmian mogą wpływać na zgodność aplikacji kierowanych na Androida 15 (poziom API 35):

  • Zmiany w interfejsach API do formatowania ciągów znaków: weryfikacja indeksu argumentu, flag, szerokości i precyzji jest teraz bardziej rygorystyczna podczas korzystania z tych interfejsów API String.format() i Formatter.format():

    Na przykład ten wyjątek jest zgłaszany, gdy używany jest indeks argumentu 0 (%0 w ciągu formatu):

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

    W takim przypadku problem można rozwiązać, używając indeksu argumentu 1 (%1 w ciągu formatu).

  • Zmiany typu komponentu Arrays.asList(...).toArray(): gdy używasz Arrays.asList(...).toArray(), typem komponentu wynikowej tablicy jest teraz Object, a nie typ elementów tablicy bazowej. Dlatego ten kod zwraca błąd ClassCastException:

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

    W tym przypadku, aby zachować String jako typ komponentu w wynikowej tablicy, możesz użyć Collection.toArray(Object[]):

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Zmiany w obsłudze kodów języków: podczas korzystania z interfejsu API Locale kody języków hebrajskiego, jidysz i indonezyjskiego nie są już konwertowane na ich przestarzałe formy (hebrajski: iw, jidysz: ji, indonezyjski: in). Podczas określania kodu języka dla jednego z tych języków używaj kodów z normy ISO 639-1 (hebrajski: he, jidysz: yi, indonezyjski: id).

  • Zmiany w losowych sekwencjach liczb całkowitych: po zmianach wprowadzonych w https://bugs.openjdk.org/browse/JDK-8301574 te metody Random.ints() zwracają teraz inną sekwencję liczb niż metody Random.nextInt():

    Zasadniczo ta zmiana nie powinna powodować problemów z działaniem aplikacji, ale Twój kod nie powinien oczekiwać, że sekwencja wygenerowana przez metody Random.ints() będzie zgodna z Random.nextInt().

Nowy interfejs API SequencedCollection może wpłynąć na zgodność aplikacji po zaktualizowaniu compileSdk w konfiguracji kompilacji aplikacji, aby używać Androida 15 (API na poziomie 35):

  • Kolizja z funkcjami rozszerzeń MutableList.removeFirst() i MutableList.removeLast() w kotlin-stdlib

    Typ List w języku Java jest mapowany na typ MutableList w języku Kotlin. Interfejsy API List.removeFirst() i List.removeLast() zostały wprowadzone w Androidzie 15 (poziom interfejsu API 35), dlatego kompilator Kotlina statycznie rozpoznaje wywołania funkcji, np. list.removeFirst(), jako nowe interfejsy API List, a nie jako funkcje rozszerzeń w kotlin-stdlib.

    Jeśli aplikacja zostanie ponownie skompilowana z ustawieniem compileSdk na 35 i minSdk na 34 lub niższym, a następnie zostanie uruchomiona na Androidzie 14 lub starszym, zostanie zgłoszony błąd środowiska wykonawczego:

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

    Istniejąca opcja analizy statycznej NewApi we wtyczce Androida do obsługi Gradle może wykrywać te nowe przypadki użycia interfejsu API.

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

    Aby rozwiązać problem z wyjątkiem czasu działania i błędami linta, wywołania funkcji removeFirst() i removeLast() można zastąpić w Kotlinie odpowiednio funkcjami removeAt(0) i removeAt(list.lastIndex). Jeśli używasz Android Studio Ladybug | 2024.1.3 lub nowszej wersji, możesz też skorzystać z opcji szybkiej naprawy tych błędów.

    Jeśli opcja lint została wyłączona, rozważ usunięcie @SuppressLint("NewApi") i lintOptions { disable 'NewApi' }.

  • Kolizja z innymi metodami w języku Java

    Do istniejących typów dodano nowe metody, np. List i Deque. Te nowe metody mogą być niezgodne z metodami o tej samej nazwie i typach argumentów w innych interfejsach i klasach. W przypadku kolizji sygnatury metody z niezgodnością kompilator javac generuje błąd czasu kompilacji. Przykład:

    Przykładowy błąd 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
    

    Przykładowy błąd 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
    

    Przykładowy błąd 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
    

    Aby naprawić te błędy kompilacji, klasa implementująca te interfejsy powinna zastąpić metodę zgodnym zwracanym typem. Przykład:

    @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 restricts the usage of TLS versions 1.0 and 1.1. These versions had previously been deprecated in Android, but are now disallowed for apps targeting Android 15.

Bezpieczne uruchamianie aktywności w tle

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

Other changes

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

Bezpieczniejsze intencje

Android 15 introduces StrictMode for intents.

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

Kotlin

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

Java

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

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.

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

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”

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.

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

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.

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

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.

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

Android 15 includes updated lists of restricted non-SDK interfaces based on collaboration with Android developers and the latest internal testing. Whenever possible, we make sure that public alternatives are available before we restrict non-SDK interfaces.

If your app does not target Android 15, some of these changes might not immediately affect you. However, while it's possible for your app to access some non-SDK interfaces depending on your app's target API level, using any non-SDK method or field always carries a high risk of breaking your app.

If you are unsure if your app uses non-SDK interfaces, you can test your app to find out. If your app relies on non-SDK interfaces, you should begin planning a migration to SDK alternatives. Nevertheless, we understand that some apps have valid use cases for using non-SDK interfaces. If you can't find an alternative to using a non-SDK interface for a feature in your app, you should request a new public 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.