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

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

Zabezpieczone uruchamianie aktywności w tle

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

Inne zmiany

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

Bezpieczniejsze 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.

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

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

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

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

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

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

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

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

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

Aparat i multimedia

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

Ograniczenia dotyczące żądania aktywności audio

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

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

Zaktualizowane ograniczenia dotyczące 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.

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.