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

Podobnie jak w przypadku poprzednich wersji, Android 15 wprowadza zmiany zachowania, które mogą mieć wpływ na Twoją aplikację. Poniższe zmiany zachowania mają zastosowanie wyłącznie do aplikacji kierowanych na Androida 15 lub nowszego. Jeśli Twoja aplikacja jest kierowana na Androida 15 lub nowszego, musisz ją odpowiednio zmodyfikować, aby obsługiwała te funkcje (w stosownych przypadkach).

Zapoznaj się też z listą zmian w działaniu, które mają wpływ na wszystkie aplikacje działające na Androidzie 15, niezależnie od tego, czy Twoja aplikacja ma targetSdkVersion.

Główna funkcja

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

Zmiany w usługach na pierwszym planie

Wprowadzamy te zmiany w usługach działających na pierwszym planie w Androidzie 15.

Zachowanie limitu czasu oczekiwania usługi działającej na pierwszym planie w ramach synchronizacji danych

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 system wywołuje metodę Service.onTimeout(int, int) (wprowadzoną w Androidzie 15). Obecnie usługa ma kilka sekund na wywołanie funkcji 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 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ą, resetując timer).
  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ę dataSync na pierwszym planie, system wyświetli komunikat o błędzie, np. „Czas limitu został już wyczerpany dla usługi na pierwszym planie typu dataSync”.ForegroundServiceStartNotAllowedException

Testowanie

Aby przetestować działanie aplikacji, możesz włączyć limity czasu synchronizacji danych, 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ć 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 na pierwszym planie 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 BOOT_COMPLETED odbiorników 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 uprawnienie SYSTEM_ALERT_WINDOW

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

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

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

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

Testowanie

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

adb shell am compat enable FGS_SAW_RESTRICTIONS your-package-name

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

Aplikacje kierowane na Androida 15 nie mogą już zmieniać globalnego stanu ani zasad trybu Nie przeszkadzać na urządzeniu (przez zmianę ustawień użytkownika lub wyłączenie trybu Nie przeszkadzać). Zamiast tego aplikacje muszą przekazywać element AutomaticZenRule, który system łączy w globalną zasadę z dotychczasowym schematem wygrywania zasad o największym ograniczeniu. Wywołania istniejących interfejsów API, które wcześniej wpływały na stan globalny (setInterruptionFilter, setNotificationPolicy), skutkują utworzeniem lub zaktualizowaniem niejawnej klasy AutomaticZenRule, która jest włączana i wyłączana w zależności od cyklu wywołań tych wywołań interfejsu API.

Pamiętaj, że ta zmiana wpływa na obserwowalne zachowanie tylko wtedy, gdy aplikacja wywołuje metodę setInterruptionFilter(INTERRUPTION_FILTER_ALL) i oczekuje, że wywołanie dezaktywacji AutomaticZenRule, która została wcześniej aktywowana przez ich właścicieli.

Zmiany w interfejsie OpenJDK API

Android 15 kontynuuje odświeżanie podstawowych bibliotek Androida, aby dostosować je do funkcji 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 do formatowania ciągów znaków: weryfikacja indeksu argumentu, flag, szerokości i dokładności jest teraz bardziej rygorystyczna podczas korzystania z tych interfejsów API: String.format()Formatter.format():

    Na przykład gdy użyjesz indeksu argumentu 0 (%0 w formatowaniu ciągu znaków), zostanie rzucone to wyjątku:

    IllegalFormatArgumentIndexException: Illegal format argument index = 0
    

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

  • Zmiany typu komponentu Arrays.asList(...).toArray(): gdy używasz funkcji Arrays.asList(...).toArray(), typ komponentu powstałego tablicy jest teraz Object, a nie typ elementów podrzędnej tablicy. Ten kod powoduje wyjątek ClassCastException:

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

    W tym przypadku, aby zachować wartość String jako typ komponentu w wynikającym z niego tablicy, możesz użyć elementu Collection.toArray(Object[]):

    String[] elements = Arrays.asList("two", "one").toArray(new String[0]);
    
  • Zmiany w obsługiwaniu kodu języka: gdy używasz interfejsu API Locale, kody języka hebrajskiego, jidysz i indonezyjskiego nie są już konwertowane do ich przestarzałych form (hebr.: iw, jidysz: ji, indonezyjski: in). Gdy określasz kod języka dla jednego z tych języków, użyj zamiast tego kodów z ISO 639-1 (hebr.: 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():

    Zwykle ta zmiana nie powinna powodować nieprawidłowego działania aplikacji, ale kod nie powinien zakładać, że sekwencja wygenerowana z metod Random.ints() będzie pasować do Random.nextInt().

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

  • Konflikt z funkcjami rozszerzenia MutableList.removeFirst()MutableList.removeLast() w pliku kotlin-stdlib

    Typ List w języku Java jest mapowany na typ MutableList w Kotlinie. Interfejsy List.removeFirst()List.removeLast() zostały wprowadzone w Androidzie 15 (poziom interfejsu API 35), dlatego kompilator Kotlina rozwiązuje wywołania funkcji, np. list.removeFirst(), statycznie do nowych interfejsów List, a nie do funkcji rozszerzeń w kotlin-stdlib.

    Jeśli aplikacja zostanie ponownie skompilowana z wartością compileSdk równą 35, a wartość minSdk równa 34 lub mniejsza, a następnie aplikacja zostanie uruchomiona na Androidzie 14 lub starszym, wystąpi błąd czasu wykonywania:

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

    Istniejąca opcja NewApi wtyczki Androida do obsługi Gradle może wykrywać 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ątki czasu wykonywania i błędy lint, możesz zastąpić wywołania funkcji removeFirst()removeLast() odpowiednio wywołaniami removeAt(0)removeAt(list.lastIndex) w Kotlinie. Jeśli używasz Android Studio Ladybug | 2024.1.3 lub nowszej wersji, możesz też szybko naprawić te błędy.

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

  • Konflikt z innymi metodami w języku Java

    Do dotychczasowych typów dodano nowe metody, np. ListDeque. 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 w czasie kompilacji. Na przykład:

    Przykład błędu 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. Na przykład:

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

Bezpieczeństwo

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

Uruchomienie bezpiecznej 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

In addition to the restriction for UID matching, these other changes are also included:

  • 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 wprowadza nowe opcjonalne środki bezpieczeństwa, które zwiększają bezpieczeństwo i stabilność intencji. Te zmiany mają na celu zapobieganie potencjalnym luk w zabezpieczeniach i niewłaściwemu wykorzystywaniu intencji, które mogą być wykorzystywane przez złośliwe aplikacje. W Androidzie 15 wprowadziliśmy 2 główne ulepszenia dotyczące bezpieczeństwa intencji:

  • Pasowanie do docelowych filtrów intencji: intencje kierowane na konkretne komponenty muszą dokładnie odpowiadać specyfikacji filtrów intencji docelowych. Jeśli wysyłasz intencję, aby uruchomić działanie innej aplikacji, docelowy komponent intencji musi być zgodny z deklarowanymi filtrami intencji działania odbierającego.
  • Intencje muszą mieć działania: intencje bez działania nie będą już pasować do żadnych filtrów intencji. Oznacza to, że intencje używane do uruchamiania działań lub usług muszą mieć wyraźnie zdefiniowane działanie.

Aby sprawdzić, jak Twoja aplikacja reaguje na te zmiany, użyj w niej metody StrictMode. Aby wyświetlić szczegółowe dzienniki dotyczące naruszeń zasad Intent, dodaj tę metodę:

Kotlin


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

Java


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

Wrażenia użytkownika i interfejs systemu

W Androidzie 15 wprowadzono kilka zmian, które mają zapewnić użytkownikom bardziej spójne i intuicyjne wrażenia.

Zmiany w oknach wstawnych

W Androidzie 15 wprowadzono 2 zmiany związane z wstawianiem okien: ustawienie od krawędzi do krawędzi jest domyślnie wymuszane w obszarze od krawędzi do krawędzi oraz zmiany w 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 przeznaczona na Androida 14, która nie jest wyświetlana od krawędzi do krawędzi na urządzeniu z Androidem 15.


Aplikacja kierowana na Androida 15 (poziom API 35) i zajmująca cały ekran na urządzeniu z Androidem 15. Ta aplikacja głównie wykorzystuje komponenty Material 3 Compose, które automatycznie stosują wstawki. Egzekwowanie zasad dostępu od brzegu do brzegu w Androidzie 15 nie wpływa negatywnie na ten ekran.

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

  • Pasek nawigacyjny z uchwytem gestów
    • Domyślnie przezroczysty.
    • Odsunięcie od dołu jest wyłączone, więc treści są wyświetlane za paskiem nawigacji systemu, chyba że zastosowano wstawki.
    • setNavigationBarColorR.attr#navigationBarColor są przestarzałe i nie wpływają na nawigację przy użyciu gestów.
    • setNavigationBarContrastEnforcedR.attr#navigationBarContrastEnforced nadal nie mają wpływu na nawigację za pomocą gestów.
  • Nawigacja przy użyciu 3 przycisków
    • Domyślnie ustawiona jest przezroczystość na poziomie 80%, a kolor może być dopasowany do tła okna.
    • Odsunięcie od dołu jest wyłączone, więc treści są wyświetlane za paskiem nawigacji systemu, chyba że zastosowano wstawki.
    • setNavigationBarColor i R.attr#navigationBarColor są domyślnie ustawione na dopasowanie do tła okna. Aby można było zastosować to ustawienie domyślne, tło okna musi być obiektem rysowalnym koloru. Ten interfejs API został wycofany, ale nadal wpływa na nawigację z 3 przyciskami.
    • setNavigationBarContrastEnforced i R.attr#navigationBarContrastEnforced mają wartość Prawda, co powoduje dodanie nieprzezroczystego tła w 80% podczas nawigacji przy użyciu 3 przycisków.
  • Pasek stanu
    • Domyślnie przezroczysty.
    • Przesunięcie od góry jest wyłączone, więc treść jest umieszczana za paskiem stanu, chyba że zostaną zastosowane zbiory eksportowe.
    • setStatusBarColorR.attr#statusBarColor są wycofane i nie mają wpływu na Androida 15.
    • setStatusBarContrastEnforced i R.attr#statusBarContrastEnforced zostały wycofane, ale nadal działają w Androidzie 15.
  • Wycięcie na wyświetlaczu
    • layoutInDisplayCutoutMode okien niepływających musi mieć wartość LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS. SHORT_EDGES, NEVERDEFAULT są interpretowane jako ALWAYS, aby użytkownicy nie widzieli czarnego paska spowodowanego wycięciem w ekranie.

Ten przykład pokazuje aplikację przed i po kierowaniu na Androida 15 (poziom API 35) oraz przed i po zastosowaniu odcięć.

Aplikacja przeznaczona na Androida 14, która nie jest wyświetlana od krawędzi do krawędzi na urządzeniu z Androidem 15.
Aplikacja, która jest kierowana na Androida 15 (poziom interfejsu API 35) i zapewnia dostęp od krawędzi do krawędzi na urządzeniu z Androidem 15. Jednak ze względu na wymóg wyświetlania bez ramki w Androidzie 15 wiele elementów jest teraz ukrytych przez pasek stanu, pasek nawigacji z 3 przyciskami lub wycięcie wyświetlacza. Ukryte elementy interfejsu to m.in. górny pasek aplikacji Material 2, pływające przyciski akcji i elementy listy.
Aplikacja kierowana na Androida 15 (poziom interfejsu API 35) działa na brzegu sieci na urządzeniu z Androidem 15 i stosuje wstawki, aby interfejs nie był ukryty.
Co sprawdzić, jeśli aplikacja jest już w trybie edge-to-edge

Jeśli Twoja aplikacja jest już w fazie od krawędzi do krawędzi i stosuje wstawki, ten problem nie wpływa na nią w większości przypadków. Wyjątkiem są poniższe scenariusze. Nawet jeśli uważasz, że problem Cię nie dotyczy, zalecamy przetestowanie aplikacji.

  • Masz okno niepł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 przez 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ą wyświetlać się ekrany o mniejszym natężeniu ruchu z zasłoniętym interfejsem. Sprawdź, czy te ekrany, które są rzadziej odwiedzane, 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 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 Cię ta zmiana. Oprócz scenariuszy dotyczących aplikacji, które są już wyświetlane na pełnym ekranie, należy wziąć pod uwagę te kwestie:

  • Jeśli Twoja aplikacja korzysta z komponentów Material 3 (androidx.compose.material3) w komponencie compose, takich jak TopAppBar, BottomAppBarNavigationBar, te komponenty prawdopodobnie nie są dotknięte, ponieważ automatycznie obsługują wstawione elementy.
  • Jeśli Twoja aplikacja korzysta w Compose z komponentów Material 2 (androidx.compose.material), te komponenty nie będą automatycznie obsługiwać wstawień. Możesz jednak uzyskać dostęp do wkładek i zastosować je ręcznie. W androidx.compose.material 1.6.0 i nowszych użyj parametru windowInsets, aby ręcznie zastosować wcięcia dla BottomAppBar, TopAppBar, BottomNavigationNavigationRail. Podobnie w przypadku Scaffold użyj parametru contentWindowInsets.
  • Jeśli Twoja aplikacja korzysta z 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 lub NavigationView, obsługuje wbudowane komponenty i nie wymaga dodatkowych działań. Jeśli używasz AppBarLayout, musisz jednak dodać android:fitsSystemWindows="true".
  • W przypadku niestandardowych funkcji kompozycyjnych zastosuj wstawki ręcznie jako dopełnienie. Jeśli Twój content znajduje się w Scaffold, możesz używać wstawionych elementów, korzystając z wartości wypełnienia Scaffold. W przeciwnym razie zastosuj wypełnienie za pomocą jednej z opcji WindowInsets.
  • Jeśli aplikacja korzysta z widoków i BottomSheet, SideSheet lub kontenerów niestandardowych, zastosuj dopełnienie za pomocą ViewCompat.setOnApplyWindowInsetsListener. W przypadku RecyclerView zastosuj wypełnienie za pomocą tego listenera i dodaj clipToPadding="false".
Co sprawdzić, czy aplikacja musi oferować niestandardową ochronę w tle

Jeśli Twoja aplikacja musi zapewniać niestandardową ochronę w tle w przypadku nawigacji z 3 przyciskami lub paska stanu, powinna umieszczać komponenty lub widoki za pomocą elementu WindowInsets.Type#tappableElement(), aby uzyskać wysokość paska nawigacji z 3 przyciskami lub elementu WindowInsets.Type#statusBars.

Dodatkowe materiały dotyczące Edge-to-Edge

Więcej informacji o ramkach w trybie pełnoekranowym znajdziesz w przewodnikach Ramki w trybie pełnoekranowymKompozycja w trybie pełnoekranowym.

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 Twoja aplikacja jest kierowana na Androida 15 (poziom API 35) lub nowszego, Configurationnie 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 odpowiednie ViewGroup, WindowInsets lub WindowMetricsCalculator, w zależności od potrzeb.

Interfejs Configuration jest dostępny od wersji API 1. Zwykle jest ona uzyskiwana z Activity.onConfigurationChanged. Zawiera on informacje o gęstości okien, orientacji i rozmiarach. Ważną cechą rozmiarów okien zwracanych z metody Configuration jest to, że wcześniej były w niej wykluczone słupki systemowe.

Rozmiar konfiguracji jest zwykle używany do wyboru zasobów, takich jak /res/layout-h500dp, i nadal jest to ważny przypadek użycia. Odradzamy jednak używanie go do obliczania układu. Jeśli tak, powinieneś się od niej oddalić. W zależności od przypadku użycia Configuration należy zastąpić czymś bardziej odpowiednim.

Jeśli używasz go do obliczania układu, użyj odpowiedniej wartości ViewGroup, np. CoordinatorLayout lub ConstraintLayout. Jeśli używasz go do określenia wysokości paska nawigacyjnego systemu, użyj WindowInsets. Jeśli chcesz poznać aktualny rozmiar okna aplikacji, użyj computeCurrentWindowMetrics.

Poniżej znajdziesz listę pól, których dotyczy ta zmiana:

AtrybuteleTextHeight ma domyślną wartość true (prawda)

W przypadku aplikacji kierowanych na Androida 15 atrybut elegantTextHeight TextView ma domyślnie wartość true, co powoduje zastąpienie domyślnie używanej domyślnie kompaktowej czcionki pewnymi skryptami o dużych danych pionowych inną, która jest znacznie bardziej czytelna. Wprowadzono kompaktową czcionkę, aby zapobiec naruszaniu układów. Android 13 (poziom interfejsu API 33) zapobiega wielu z tych problemów, umożliwiając układowi tekstu rozciąganie wysokości w pionie za pomocą atrybutu fallbackLineSpacing.

W Androidzie 15 kompaktowa czcionka pozostaje w systemie, więc aplikacja może ustawić elegantTextHeight na false, aby uzyskać takie samo działanie jak wcześniej, ale prawdopodobnie nie będzie obsługiwana w kolejnych wersjach. Jeśli Twoja aplikacja obsługuje te skrypty: arabski, laotański, birmański, tamilski, gudżarati, kannada, malajalam, orija, telugu lub tajski, przetestuj ją, ustawiając w polu elegantTextHeight opcję true.

Zachowanie elegantTextHeight w przypadku aplikacji kierowanych na Androida 14 (poziom API 34) i starsze.
elegantTextHeight w przypadku aplikacji kierowanych na Androida 15.

Zmiany szerokości obiektu 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 dla EditText uwzględniająca lokalizację

W poprzednich wersjach Androida układ tekstu rozciągnął wysokość tekstu, aby dopasować ją do wysokości czcionki w bieżącym języku. Jeśli na przykład treść była w języku japońskim, ponieważ wysokość wiersza czcionki japońskiej jest nieco większa niż czcionki łacińskiej, wysokość tekstu znacznie się zwiększyła. Pomimo tych różnic w wysokości wierszy, rozmiar elementu EditText był jednak taki sam niezależnie od używanego regionu, jak na tym przykładzie:

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

W przypadku aplikacji kierowanych na Androida 15 minimalna wysokość wiersza jest teraz zarezerwowana dla EditText, aby pasowała do czcionki referencyjnej dla określonego języka, jak widać na tym obrazie:

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

W razie potrzeby aplikacja może przywrócić poprzednie działanie, określając atrybut useLocalePreferredLineHeightForMinimum jako false, a aplikacja może ustawiać niestandardowe minimalne wskaźniki branżowe za pomocą interfejsu API setMinimumFontMetrics w Kotlin i Javie.

Aparat i multimedia

W Androidzie 15 wprowadzono następujące zmiany w zachowaniu aparatu i multimediów w aplikacjach kierowanych na Androida 15 lub nowszego.

Ograniczenia dotyczące żądania skupienia się na dźwięku

Aplikacje kierowane na Androida 15 muszą być najlepszą aplikacją lub obsługiwać usługę na pierwszym planie, aby żądać aktywności audio. Jeśli aplikacja próbuje poprosić o koncentrację, gdy nie spełnia jednego z tych wymagań, wywołanie zwraca AUDIOFOCUS_REQUEST_FAILED.

Więcej informacji o aktywności audio znajdziesz w artykule Zarządzanie aktywnością audio.

Zaktualizowane ograniczenia inne niż związane z pakietem SDK

Android 15 zawiera zaktualizowane listy ograniczonych interfejsów innych niż interfejsy SDK na podstawie współpracy z deweloperami Androida i najnowszych testów wewnętrznych. Zawsze, gdy to możliwe, sprawdzamy, czy dostępne są publiczne alternatywy, zanim zaczniemy ograniczać interfejsy inne niż SDK.

Jeśli Twoja aplikacja nie jest kierowana na Androida 15, niektóre z tych zmian mogą nie od razu Cię dotyczyć. Chociaż aplikacja może uzyskiwać dostęp do niektórych interfejsów spoza pakietu SDK w zależności od docelowego poziomu interfejsu API, korzystanie z metod lub pól spoza pakietu SDK zawsze wiąże się z wysokim ryzykiem awarii aplikacji.

Jeśli nie masz pewności, czy Twoja aplikacja używa interfejsów innych niż SDK, możesz ją przetestować. Jeśli Twoja aplikacja korzysta z interfejsów spoza pakietu SDK, zaplanuj migrację na alternatywne pakiety SDK. Zdajemy sobie jednak sprawę, że w niektórych przypadkach interfejsy inne niż SDK mogą być przydatne. Jeśli nie możesz znaleźć w swojej aplikacji interfejsu innego niż interfejs SDK, musisz poprosić o nowy publiczny interfejs 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.