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 kontynuuje prace nad odświeżaniem podstawowych bibliotek Androida, aby były one zgodne z funkcjami w najnowszych wersjach OpenJDK LTS.

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

  • Zmiany w interfejsach API formatowania ciągów znaków: w przypadku korzystania z tych interfejsów API walidacja indeksu argumentu, flag, szerokości i precyzji jest teraz bardziej rygorystyczna: 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 tym przypadku problem można rozwiązać, używając indeksu argumentu 1 (%1 w ciągu formatu).

  • Zmiany w typie 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 zgłasza 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[]) zamiast tego:

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Zmiany w obsłudze kodów języków: gdy używasz 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 jednej z tych ustawień regionalnych używaj kodów z ISO 639-1 (hebrajski: he, jidysz: yi, indonezyjski: id).

  • Zmiany w sekwencjach liczb losowych: 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ć nieprawidłowego działania 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 mieć wpływ na zgodność aplikacji po zaktualizowaniu compileSdk w konfiguracji kompilacji aplikacji, aby używać Androida 15 (poziom interfejsu API 35):

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

    Typ List w Javie jest mapowany na typ MutableList w Kotlinie. Ponieważ interfejsy API List.removeFirst() i List.removeLast() zostały wprowadzone w Androidzie 15 (poziom interfejsu API 35), kompilator Kotlin rozwiązuje wywołania funkcji, np. list.removeFirst(), statycznie do nowych interfejsów API List, a nie do funkcji rozszerzeń w kotlin-stdlib.

    Jeśli aplikacja zostanie ponownie skompilowana z compileSdk ustawionym na 35 i minSdk ustawionym na 34 lub niższą wartość, 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 lint NewApi we wtyczce Androida do obsługi Gradle może wykryć te nowe 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 naprawić wyjątek środowiska wykonawczego i błędy lint, wywołania funkcji removeFirst() i removeLast() można zastąpić odpowiednio removeAt(0) i removeAt(list.lastIndex) w Kotlinie. Jeśli używasz Androida Studio Ladybug | 2024.1.3 lub nowszego, udostępnia ono też opcję 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 Javie

    Do istniejących typów, np., List i Deque, dodano nowe metody. 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 zgłasza błąd w czasie 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 typem zwracanym. 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 złośliwymi aplikacjami.

Ograniczone wersje protokołu 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.

Zabezpieczone 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 zamiary

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

Wrażenia użytkowników i interfejs systemu

Android 15 zawiera kilka zmian, które mają na celu zapewnienie bardziej spójnej i intuicyjnej obsługi.

Zmiany w oknie

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

Jeśli aplikacja jest kierowana na Androida 15 (poziom API 35), domyślnie wyświetla się bez ramki na urządzeniach z tą wersją systemu.

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), która wyświetla się bez ramki na urządzeniu z Androidem 15. Ta aplikacja używa głównie komponentów Compose z Material 3 które automatycznie stosują wcięcia. Egzekwowanie zasad dotyczących wyświetlania bez ramki w Androidzie 15 nie ma negatywnego wpływu na ten ekran.

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

  • Pasek nawigacji z obsługą gestów
    • Domyślnie przezroczysty.
    • Odsunięcie od dołu jest wyłączone, więc treść jest rysowana za paskiem nawigacji systemu, chyba że zastosowano wcięcia.
    • 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 ustawiona jest przezroczystość 80%, a kolor może być zgodny z tłem okna.
    • Odsunięcie od dołu jest wyłączone, więc treść jest rysowana za paskiem nawigacji systemu, chyba że zastosowano wcięcia.
    • setNavigationBarColor i R.attr#navigationBarColor są domyślnie ustawione tak, aby pasowały do tła okna. Aby zastosować to ustawienie domyślne, tło okna musi być obiektem rysowalnym w kolorze. Ten interfejs API jest wycofany, ale nadal wpływa na nawigację przy użyciu 3 przycisków.
    • setNavigationBarContrastEnforced i R.attr#navigationBarContrastEnforced są domyślnie ustawione na wartość „prawda”, co powoduje dodanie tła o przezroczystości 80% w nawigacji przy użyciu 3 przycisków.
  • Pasek stanu
    • Domyślnie przezroczysty.
    • Odsunięcie od góry jest wyłączone, więc treść jest rysowana za paskiem stanu, chyba że zastosowano wcięcia.
    • setStatusBarColor i R.attr#statusBarColor są wycofane i nie mają wpływu na Androida 15.
    • setStatusBarContrastEnforced i R.attr#statusBarContrastEnforced są wycofane, ale nadal mają wpływ na Androida 15.
  • Wycięcie w ekranie
    • W przypadku okien niepływających wartość layoutInDisplayCutoutMode musi być równa LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Wartości SHORT_EDGES, NEVER i DEFAULT są interpretowane jako ALWAYS, dzięki czemu użytkownicy nie widzą czarnego paska spowodowanego wycięciem w ekranie, a aplikacja wyświetla się bez ramki.

Poniższy przykład pokazuje aplikację przed i po kierowaniu na Androida 15 (poziom API 35) oraz przed i po zastosowaniu wcięć. 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 (poziom API 35), która wyświetla się bez ramki na urządzeniu z Androidem 15. Jednak wiele elementów jest teraz ukrytych przez pasek stanu , pasek nawigacji przy użyciu 3 przycisków lub wycięcie w ekranie ze względu na egzekwowanie zasad dotyczących wyświetlania bez ramki w Androidzie 15. Ukryty interfejs obejmuje górny pasek aplikacji Material 2 , pływające przyciski czynności i elementy listy.
Aplikacja kierowana na Androida 15 (poziom API 35), która wyświetla się bez ramki na urządzeniu z Androidem 15 i stosuje wcięcia, dzięki czemu interfejs nie jest ukryty.
Co sprawdzić, jeśli aplikacja już wyświetla się bez ramki

Jeśli Twoja aplikacja już wyświetla się bez ramki i stosuje wcięcia, w większości przypadków nie musisz nic robić, z wyjątkiem tych sytuacji: Nawet jeśli uważasz, że nie musisz nic robić, zalecamy przetestowanie aplikacji.

  • Masz okno niepływające, np. Activity, które używa wartości SHORT_EDGES, NEVER lub DEFAULT zamiast LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. Jeśli aplikacja ulega awarii podczas uruchamiania, przyczyną może być ekran powitalny. Możesz zaktualizować 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 wprowadzające lub logowania,
    • strony ustawień.
Co sprawdzić, jeśli aplikacja nie wyświetla się jeszcze bez ramki

Jeśli Twoja aplikacja nie wyświetla się jeszcze bez ramki, najprawdopodobniej musisz coś zmienić. Oprócz scenariuszy dotyczących aplikacji, które już wyświetlają się bez ramki, musisz wziąć 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, te komponenty nie powinny mieć wpływu, ponieważ automatycznie obsługują wcięcia.
  • Jeśli Twoja 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 wcięć i zastosować je ręcznie. W androidx.compose.material 1.6.0 i nowszych wersjach użyj parametru windowInsets, aby ręcznie zastosować wcięcia w przypadku komponentów BottomAppBar, TopAppBar, BottomNavigation i NavigationRail. Podobnie użyj parametru contentWindowInsets dla Scaffold.
  • Jeśli Twoja aplikacja używa widoków i komponentów Material (com.google.android.material), większość komponentów Material opartych na widokach, takich jak BottomNavigationView, BottomAppBar, NavigationRailView czy NavigationView, obsługuje wcięcia i nie wymaga dodatkowej pracy. Jeśli jednak używasz komponentu AppBarLayout, musisz dodać android:fitsSystemWindows="true".
  • W przypadku niestandardowych komponentów kompozycyjnych zastosuj wcięcia ręcznie jako dopełnienie. Jeśli treść znajduje się w komponencie Scaffold, możesz użyć wcięć za pomocą wartości dopełnienia Scaffold padding values. W przeciwnym razie zastosuj dopełnienie za pomocą jednego z komponentów WindowInsets.
  • Jeśli Twoja aplikacja używa widoków i BottomSheet, SideSheet lub niestandardowych kontenerów, zastosuj dopełnienie za pomocą ViewCompat.setOnApplyWindowInsetsListener. W przypadku RecyclerView zastosuj dopełnienie za pomocą tego odbiornika i dodaj też clipToPadding="false".
Co sprawdzić, jeśli aplikacja musi oferować niestandardową ochronę tła

Jeśli Twoja aplikacja musi oferować niestandardową ochronę tła w przypadku nawigacji przy użyciu 3 przycisków lub paska stanu, powinna umieścić komponent kompozycyjny lub widok za paskiem systemowym za pomocą WindowInsets.Type#tappableElement(), aby uzyskać wysokość paska nawigacji przy użyciu 3 przycisków, lub WindowInsets.Type#statusBars.

Dodatkowe materiały dotyczące wyświetlania bez ramki

Dodatkowe informacje o stosowaniu wcięć znajdziesz w przewodnikach Wyświetlanie bez ramki w widokach i Wyświetlanie bez ramki w Compose.

Wycofane interfejsy API

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

Te interfejsy API są wycofane i wyłączone:

Stabilna konfiguracja

Jeśli Twoja 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 klasy Configuration, zastąp ją lepszymi alternatywami, takimi jak odpowiednia klasa ViewGroup, WindowInsets lub WindowMetricsCalculator, w zależności od potrzeb.

Configuration jest dostępny od interfejsu API 1. Zwykle jest ona uzyskiwana z Activity.onConfigurationChanged. Zawiera informacje takie jak gęstość okien, orientacja i rozmiary. Ważną cechą rozmiarów okien zwracanych przez Configuration jest to, że wcześniej wykluczały one paski systemowe.

Rozmiar konfiguracji jest zwykle używany do wybierania zasobów, np. /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 jest, odsuń się od niego. Zamiast Configuration użyj czegoś bardziej odpowiedniego w zależności od konkretnego przypadku użycia.

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

Poniżej znajdziesz listę pól, 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.

Szerokość widoku TextView zmienia się 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 widoku EditText z uwzględnieniem 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 aktywności 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 pakietów SDK innych firm

Android 15 zawiera zaktualizowane listy ograniczonych interfejsów innych niż SDK, które powstały na podstawie współpracy z deweloperami Androida i najnowszych testów wewnętrznych. Zawsze, gdy jest to możliwe, udostępniamy publiczne alternatywy, zanim ograniczymy interfejsy inne niż SDK.

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

Jeśli nie masz pewności, czy Twoja aplikacja korzysta z interfejsów innych niż SDK, możesz sprawdzić to, testując aplikację. Jeśli Twoja aplikacja korzysta z interfejsów innych niż SDK, zacznij planować migrację do alternatywnych pakietów SDK. Rozumiemy jednak, że niektóre aplikacje mają uzasadnione przypadki użycia interfejsów innych niż SDK. Jeśli nie możesz znaleźć alternatywy dla używania interfejsu innego niż SDK w przypadku funkcji w aplikacji, powinieneś poprosić o nowy publiczny interfejs API.

To learn more about the changes in this release of Android, see Updates to non-SDK interface restrictions in Android 15. To learn more about non-SDK interfaces generally, see Restrictions on non-SDK interfaces.