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
- Nowy typ usługi na pierwszym planie do przetwarzania multimediów
- Ograniczenia dotyczące
BOOT_COMPLETED
odbiorników uruchamiających usługi na pierwszym planie - Ograniczenia uruchamiania usług na pierwszym planie, gdy aplikacja ma uprawnienie
SYSTEM_ALERT_WINDOW
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 mediaProcessing
typu 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:
- Zaimplementuj nową metodę
Service.onTimeout(int, int)
w swojej usłudze. Gdy aplikacja otrzyma połączenie zwrotne, w ciągu kilku sekund zadzwoń pod numerstopSelf()
. Jeśli nie zatrzymasz aplikacji od razu, system wygeneruje błąd. - 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). - 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. - 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:
- Zaimplementuj nową metodę
Service.onTimeout(int, int)
w swojej usłudze. Gdy aplikacja otrzyma połączenie zwrotne, w ciągu kilku sekund zadzwoń pod numerstopSelf()
. (jeśli nie zatrzymasz aplikacji od razu, system wygeneruje błąd). - 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). - 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. - 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:
dataSync
camera
mediaPlayback
phoneCall
mediaProjection
microphone
(to ograniczenie obowiązuje w przypadkumicrophone
od Androida 14)
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()
iFormatter.format()
:String.format(String, Object[])
String.format(Locale, String, Object[])
Formatter.format(String, Object[])
Formatter.format(Locale, String, Object[])
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 funkcjiArrays.asList(...).toArray()
, typ komponentu powstałego tablicy jest terazObject
, a nie typ elementów podrzędnej tablicy. Ten kod powoduje wyjątekClassCastException
: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ć elementuCollection.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ż metodyRandom.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ć doRandom.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()
iMutableList.removeLast()
w plikukotlin-stdlib
Typ
List
w języku Java jest mapowany na typMutableList
w Kotlinie. InterfejsyList.removeFirst()
iList.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ówList
, a nie do funkcji rozszerzeń wkotlin-stdlib
.Jeśli aplikacja zostanie ponownie skompilowana z wartością
compileSdk
równą35
, a wartośćminSdk
równa34
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()
iremoveLast()
odpowiednio wywołaniamiremoveAt(0)
iremoveAt(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")
ilintOptions { disable 'NewApi' }
.Konflikt z innymi metodami w języku Java
Do dotychczasowych typów dodano nowe metody, np.
List
iDeque
. 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ą kompilatorjavac
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 ListPrzykł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 errorPrzykł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 errorAby 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 aPendingIntent
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).
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.
setNavigationBarColor
iR.attr#navigationBarColor
są przestarzałe i nie wpływają na nawigację przy użyciu gestów.setNavigationBarContrastEnforced
iR.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
iR.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
iR.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.
setStatusBarColor
iR.attr#statusBarColor
są wycofane i nie mają wpływu na Androida 15.setStatusBarContrastEnforced
iR.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
,NEVER
iDEFAULT
są interpretowane jakoALWAYS
, 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ęć.
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żywaSHORT_EDGES
,NEVER
lubDEFAULT
zamiastLAYOUT_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 jakTopAppBar
,BottomAppBar
iNavigationBar
, 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 parametruwindowInsets
, aby ręcznie zastosować wcięcia dlaBottomAppBar
,TopAppBar
,BottomNavigation
iNavigationRail
. Podobnie w przypadkuScaffold
użyj parametrucontentWindowInsets
. - 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 jakBottomNavigationView
,BottomAppBar
,NavigationRailView
lubNavigationView
, obsługuje wbudowane komponenty i nie wymaga dodatkowych działań. Jeśli używaszAppBarLayout
, 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łnieniaScaffold
. W przeciwnym razie zastosuj wypełnienie za pomocą jednej z opcjiWindowInsets
. - Jeśli aplikacja korzysta z widoków i
BottomSheet
,SideSheet
lub kontenerów niestandardowych, zastosuj dopełnienie za pomocąViewCompat.setOnApplyWindowInsetsListener
. W przypadkuRecyclerView
zastosuj wypełnienie za pomocą tego listenera i dodajclipToPadding="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łnoekranowym i Kompozycja w trybie pełnoekranowym.
Wycofane interfejsy API
Te interfejsy API zostały wycofane, ale nie zostały wyłączone:
R.attr#enforceStatusBarContrast
R.attr#navigationBarColor
(dla nawigacji przy użyciu 3 przycisków z 80% alfa)Window#isStatusBarContrastEnforced
Window#setNavigationBarColor
(nawigacja przy użyciu 3 przycisków, przezroczystość 80%)Window#setStatusBarContrastEnforced
Te interfejsy API zostały wycofane i wyłączone:
R.attr#navigationBarColor
(do nawigacji przy użyciu gestów)R.attr#navigationBarDividerColor
R.attr#statusBarColor
Window#setDecorFitsSystemWindows
Window#getNavigationBarColor
Window#getNavigationBarDividerColor
Window#getStatusBarColor
Window#setNavigationBarColor
(do nawigacji przy użyciu gestów)Window#setNavigationBarDividerColor
Window#setStatusBarColor
Stabilna konfiguracja
Jeśli Twoja aplikacja jest kierowana na Androida 15 (poziom API 35) lub nowszego, Configuration
nie wyklucza już pasków systemowych. Jeśli do obliczania układu używasz rozmiaru ekranu w klasie Configuration
, zastąp go lepszymi alternatywami, takimi jak 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:
- Rozmiary
Configuration.screenWidthDp
iscreenHeightDp
nie wykluczają już pasków systemu. - Zmiany w usługach
screenWidthDp
iscreenHeightDp
mają pośredni wpływ na usługęConfiguration.smallestScreenWidthDp
. Configuration.orientation
jest pośrednio zależny od zmian wscreenWidthDp
iscreenHeightDp
na urządzeniach z ekranem zbliżonym do kwadratu.Display.getSize(Point)
jest pośrednio dotknięty zmianami wConfiguration
. Został on wycofany od poziomu API 30.- Funkcja
Display.getMetrics()
działa już w ten sposób od poziomu API 33.
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
.
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.
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:
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:
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.