Oprócz nowych funkcji i możliwości Android 8.0 (poziom interfejsu API 26) wprowadza wiele zmian w zachowaniu systemu i interfejsu API. W tym dokumencie omawiamy najważniejsze zmiany, które należy wziąć pod uwagę w swoich aplikacjach.
Większość tych zmian dotyczy wszystkich aplikacji, niezależnie od tego, na którą wersję Androida są one kierowane. Jednak niektóre zmiany dotyczą tylko aplikacji kierowanych na Androida 8.0. Aby zwiększyć przejrzystość, podzieliliśmy tę stronę na 2 sekcje: Zmiany dla wszystkich aplikacji i Zmiany dla aplikacji kierowanych na Androida 8.0.
Zmiany we wszystkich aplikacjach
Te zmiany zachowania dotyczą
Limity wykonywania w tle
Jednym z modyfikacji wprowadzonych w Androidzie 8.0 (poziom interfejsu API 26) w celu wydłużenia czasu pracy na baterii jest to, że gdy aplikacja przechodzi w stan cached (zapisany w pamięci podręcznej) bez aktywnych komponentów, system zwalnia wszystkie blokady budzenia, które aplikacja posiada.
Aby zwiększyć wydajność urządzenia, system ogranicza też niektóre działania aplikacji, które nie działają na pierwszym planie. Więcej szczegółów:
- Aplikacje działające w tle mają teraz ograniczony dostęp do usług działających w tle.
- Aplikacje nie mogą używać swoich plików manifestu do rejestrowania większości komunikatów niejawnych (czyli transmisji, które nie są na nie kierowane).
Domyślnie te ograniczenia dotyczą tylko aplikacji kierowanych na O. Użytkownicy mogą jednak włączyć te ograniczenia w dowolnej aplikacji na ekranie Ustawienia, nawet jeśli aplikacja nie jest kierowana na O.
Android 8.0 (poziom interfejsu API 26) zapewnia też te zmiany w konkretnych metodach:
- Metoda
startService()
powoduje teraz błądIllegalStateException
, jeśli aplikacja kierowana na Androida 8.0 próbuje użyć tej metody w sytuacji, gdy nie może utworzyć usług działających w tle. - Nowa metoda
Context.startForegroundService()
uruchamia usługę na pierwszym planie. System zezwala aplikacjom na wywoływanieContext.startForegroundService()
nawet wtedy, gdy aplikacja działa w tle. Aplikacja musi jednak wywołać metodęstartForeground()
tej usługi w ciągu 5 sekund od jej utworzenia.
Więcej informacji znajdziesz w artykule Limity wykonywania w tle.
Limity lokalizacji w tle na Androidzie
Aby oszczędzać baterię, dbać o wygodę użytkowników i stan systemu, aplikacje działające w tle otrzymują aktualizacje lokalizacji rzadziej, gdy są używane na urządzeniu z Androidem 8.0. Ta zmiana działania ma wpływ na wszystkie aplikacje, które otrzymują aktualizacje lokalizacji, w tym na Usługi Google Play.
Te zmiany dotyczą tych interfejsów API:
- Dostawca uśrednionej lokalizacji (FLP)
- Geofencing
- Pomiary GNSS
- Menedżer lokalizacji
- Menedżer Wi-Fi
Aby mieć pewność, że aplikacja działa zgodnie z oczekiwaniami, wykonaj te czynności:
- Sprawdź logikę swojej aplikacji i upewnij się, że używasz najnowszych interfejsów API lokalizacji.
- Sprawdź, czy aplikacja zachowuje się zgodnie z oczekiwaniami w każdym przypadku użycia.
- Rozważ użycie połączonego dostawcy lokalizacji (FLP) lub geofencingu, aby obsłużyć przypadki użycia, które zależą od bieżącej lokalizacji użytkownika.
Więcej informacji o tych zmianach znajdziesz w artykule Ograniczenia dostępu do lokalizacji w tle.
Skróty do aplikacji
Android 8.0 (poziom interfejsu API 26) zawiera te zmiany dotyczące skrótów aplikacji:
- Transmisja
com.android.launcher.action.INSTALL_SHORTCUT
nie ma już wpływu na Twoją aplikację, ponieważ jest teraz prywatną, domyślną transmisją. Zamiast tego utwórz skrót do aplikacji, korzystając z metodyrequestPinShortcut()
z klasyShortcutManager
. - Intencją
ACTION_CREATE_SHORTCUT
można teraz tworzyć skróty aplikacji, którymi zarządzasz za pomocą klasyShortcutManager
. Ta intencja może też tworzyć skróty w starszych wersjach programu uruchamiającego, które nie współpracują zShortcutManager
. Wcześniej ta intencja mogła tworzyć tylko skróty w starszych wersjach programu uruchamiającego. - Skróty utworzone za pomocą funkcji
requestPinShortcut()
oraz skróty utworzone w aktywności, która obsługuje intencjęACTION_CREATE_SHORTCUT
są teraz pełnoprawnymi skrótami aplikacji. W związku z tym aplikacje mogą teraz aktualizować je za pomocą metod zShortcutManager
. - Stare skróty zachowują funkcjonalność z poprzednich wersji Androida, ale musisz je ręcznie przekonwertować na skróty do aplikacji.
Więcej informacji o zmianach w skrótach do aplikacji znajdziesz w przewodniku po funkcji Przypinanie skrótów i widżetów.
Języki i internacjonalizacja
W Androidzie 7.0 (poziom interfejsu API 24) wprowadzono możliwość określenia domyślnego języka kategorii, ale niektóre interfejsy API nadal korzystały z ogólnej metody Locale.getDefault()
bez argumentów, podczas gdy w ich przypadku należałoby użyć domyślnego języka kategorii DISPLAY
. W Androidzie 8.0 (poziom interfejsu API 26) w tych metodach zamiast Locale.getDefault()
jest używana wartość Locale.getDefault(Category.DISPLAY)
:
Funkcja Locale.getDisplayScript(Locale)
przechodzi też do wartości Locale.getDefault()
, gdy wartość displayScript podana dla argumentu Locale
jest niedostępna.
Dodatkowe zmiany związane z lokalizacją i międzynarodowością:
- Wywołanie
Currency.getDisplayName(null)
powoduje błądNullPointerException
, co odpowiada udokumentowanemu działaniu. - Zmieniono analizowanie nazwy strefy czasowej. Wcześniej urządzenia z Androidem używały wartości zegara systemowego pobranej podczas uruchamiania, aby przechowywać w pamięci podręcznej nazwy stref czasowych używanych do analizowania dat i czasów. W efekcie może to negatywnie wpłynąć na parsowanie, jeśli zegar systemowy był nieprawidłowy podczas uruchamiania lub w innych rzadszych przypadkach.
Obecnie w typowych przypadkach logika analizowania używa ICU i obecnej wartości zegara systemowego podczas analizowania nazw stref czasowych. Ta zmiana zapewnia bardziej prawidłowe wyniki, które mogą się różnić od wyników z wcześniejszych wersji Androida, gdy aplikacja używa klas takich jak
SimpleDateFormat
. - Android 8.0 (poziom interfejsu API 26) aktualizuje wersję ICU do wersji 58.
Okna alertów
Jeśli aplikacja korzysta z uprawnienia SYSTEM_ALERT_WINDOW
i używa jednego z tych typów okien, aby wyświetlać okna alertów nad innymi aplikacjami i oknami systemowymi:
...to te okna zawsze będą się pojawiać pod oknami, które używają typu okna TYPE_APPLICATION_OVERLAY
. Jeśli aplikacja jest kierowana na Androida 8.0 (poziom interfejsu API 26), aplikacja używa typu okna TYPE_APPLICATION_OVERLAY
do wyświetlania okien alertów.
Więcej informacji znajdziesz w sekcji Typowe typy okien alertów w artykule o zmianach działania aplikacji kierowanych na Androida 8.0.
Wpisywanie i poruszanie się po ekranie
Wraz z postępem aplikacji na Androida w ChromeOS i na innych dużych urządzeniach, takich jak tablety, obserwujemy ponowny wzrost korzystania z nawigacji za pomocą klawiatury w aplikacjach na Androida. W Androidzie 8.0 (poziom interfejsu API 26) ponownie zajęliśmy się kwestią używania klawiatury jako urządzenia do wprowadzania danych nawigacyjnych. W efekcie udało nam się stworzyć bardziej niezawodny i przewidywalny model nawigacji za pomocą strzałek i kart.
Wprowadziliśmy te zmiany w zachowaniu dotyczącego fokusowania elementów:
-
Jeśli nie zdefiniujesz żadnych kolorów stanu skupienia dla obiektu
View
(czy to pierwszego planu, czy tła), framework ustawi domyślny kolor wyróżnienia skupienia dlaView
. To podświetlenie jest elementem rysowanym falą, który zmienia się w zależności od motywu aktywności.Jeśli nie chcesz, aby obiekt
View
używał domyślnego podświetlenia po uzyskaniu fokusa, ustaw atrybutandroid:defaultFocusHighlightEnabled
nafalse
w pliku XML układu zawierającym elementView
lub przekaż wartośćfalse
do elementusetDefaultFocusHighlightEnabled()
w logice interfejsu użytkownika aplikacji. - Aby sprawdzić, jak dane wprowadzane za pomocą klawiatury wpływają na skupienie na elemencie interfejsu, możesz włączyć opcję dla programistów Rysowanie > Pokaż granice układu. W Androidzie 8.0 ta opcja wyświetla ikonę „X” nad aktualnie zaznaczonym elementem.
Wszystkie elementy paska narzędzi w Androidzie 8.0 są automatycznie grupowane na klawiaturze, co ułatwia użytkownikom nawigację po poszczególnych paskach narzędzi.
Aby dowiedzieć się więcej o ulepszaniu obsługi nawigacji za pomocą klawiatury w aplikacji, przeczytaj przewodnik Wsparcie nawigacji za pomocą klawiatury.
Autouzupełnianie formularzy internetowych
Teraz, gdy Framework autouzupełniania na Androida zapewnia wbudowane wsparcie dla funkcji autouzupełniania, w przypadku aplikacji zainstalowanych na urządzeniach z Androidem 8.0 (poziom interfejsu API 26) zmieniły się następujące metody związane z obiektmi WebView
:
WebSettings
-
- Metoda
getSaveFormData()
zwraca terazfalse
. Wcześniej zwracała ona zamiast tego wartośćtrue
. - Wywołanie
setSaveFormData()
nie działa już.
- Metoda
WebViewDatabase
-
- Wywołanie
clearFormData()
nie działa już. - Metoda
hasFormData()
zwraca teraz wartośćfalse
. Wcześniej ta metoda zwracałatrue
, gdy formularz zawierał dane.
- Wywołanie
Ułatwienia dostępu
Android 8.0 (poziom interfejsu API 26) zawiera te zmiany dotyczące ułatwień dostępu:
-
Platforma dostępności konwertuje teraz wszystkie gesty podwójnego dotknięcia w akcje
ACTION_CLICK
. Ta zmiana sprawi, że TalkBack będzie działać bardziej jak inne usługi ułatwień dostępu.Jeśli obiekty
View
w aplikacji korzystają z niestandardowego obsługiwania dotyku, sprawdź, czy nadal działają z TalkBack. Wystarczy, że zarejestrujesz moduł obsługi kliknięcia, którego używają obiektyView
. Jeśli TalkBack nadal nie rozpoznaje gestów wykonywanych na tych obiektachView
, zastąpij jeperformAccessibilityAction()
. - Usługi ułatwień dostępu są teraz świadome wszystkich wystąpień
ClickableSpan
w obiektachTextView
w aplikacji.
Więcej informacji o zwiększaniu dostępności aplikacji znajdziesz w artykule Ułatwienia dostępu.
Sieć i połączenia HTTP(S)
Android 8.0 (poziom interfejsu API 26) zawiera te zmiany zachowania dotyczące sieci i połączeń HTTP(S):
- Żądania OPTIONS bez treści mają nagłówek
Content-Length: 0
. Wcześniej nie miały nagłówkaContent-Length
. - HttpURLConnection normalizuje adresy URL zawierające puste ścieżki, dodając ukośnik po nazwie hosta lub autorytetu. Na przykład:
http://example.com
jest przekształcane whttp://example.com/
. - Niestandardowy selektor serwera proxy ustawiony za pomocą funkcji ProxySelector.setDefault() jest kierowany tylko na adres (schemat, host i port) żądanego adresu URL. W rezultacie wybór serwera proxy może być oparty tylko na tych wartościach. Adres URL przekazany niestandardowemu selektorowi serwera proxy nie zawiera ścieżki, parametrów zapytania ani fragmentów żądanego adresu URL.
- Identyfikatory URI nie mogą zawierać pustych etykiet.
Wcześniej platforma obsługiwała obejście polegające na akceptowanie pustych etykiet w nazwach hostów, co jest niedozwolone w przypadku identyfikatorów URI. To obejście miało na celu zapewnienie zgodności ze starszymi wersjami libcore. Deweloperzy korzystający z interfejsu API w nieprawidłowy sposób zobaczą komunikat ADB: „Identyfikator URI example..com zawiera puste etykiety w nazwie hosta. Jest on źle sformatowany i nie będzie akceptowany w przyszłych wersjach Androida." W Androidzie 8.0 nie można już stosować tego obejścia. W przypadku nieprawidłowego identyfikatora URI system zwraca wartość null.
- Implementacja klasy HttpsURLConnection w Androidzie 8.0 nie korzysta z niebezpiecznej wersji protokołu TLS/SSL.
- Obsługa tunelowania połączeń HTTP(S) zmieniła się w ten sposób:
- Podczas tunelowania połączenia HTTPS przez połączenie system prawidłowo umieszcza numer portu (:443) w wierszu Host, wysyłając te informacje do serwera pośredniego. Wcześniej numer portu występował tylko w wierszu CONNECT.
- System nie wysyła już nagłówków user-agent i proxy-authorization z tunelowanego żądania do serwera proxy.
Podczas konfigurowania tunelu system nie wysyła już do serwera proxy nagłówka autoryzacji serwera proxy w tunelu Http(s)URLConnection do serwera proxy. Zamiast tego system generuje nagłówek proxy-authorization i wysyła go do serwera proxy, gdy ten wysyła odpowiedź HTTP 407 w odpowiedzi na początkowe żądanie.
System nie będzie już kopiować nagłówka klienta użytkownika z żądania tunelowanego do żądania serwera proxy, które konfiguruje tunel. Zamiast tego biblioteka generuje nagłówek klienta użytkownika dla tego żądania.
- Jeśli wcześniej wykonana metoda connect() zakończyła się niepowodzeniem, metoda
send(java.net.DatagramPacket)
zgłasza SocketException.- Jeśli wystąpi błąd wewnętrzny, DatagramSocket.connect() ustawia pendingSocketException. W wersjach Androida starszych niż 8.0 wywołanie funkcji recv() wywoływało SocketException, mimo że wywołanie funkcji send() zakończyło się powodzeniem. W trosce o spójność oba wywołania rzucają teraz SocketException.
- InetAddress.isReachable() próbuje użyć protokołu ICMP, a potem, jeśli to nie zadziała, używa protokołu TCP Echo.
- Niektóre hosty, które blokują port 7 (TCP Echo), takie jak google.com, mogą stać się dostępne, jeśli akceptują protokół ICMP Echo.
- W przypadku hostów, których nie można dosięgnąć, ta zmiana oznacza, że przed powrotem wywołania mija podwójna ilość czasu.
Bluetooth
W Androidzie 8.0 (poziom interfejsu API 26) wprowadzono następujące zmiany długości danych zwracanych przez metodę ScanRecord.getBytes()
:
- Metoda
getBytes()
nie zakłada żadnej liczby bajtów. Dlatego aplikacje nie powinny polegać na minimalnej ani maksymalnej liczbie zwracanych bajtów. Zamiast tego należy sprawdzić długość tablicy wynikowej. - Urządzenia zgodne z Bluetooth 5 mogą zwracać dane o długości przekraczającej poprzedni maksymalny limit około 60 bajtów.
- Jeśli urządzenie zdalne nie odpowie na skanowanie, może zwrócić mniej niż 60 bajtów.
Bezproblemowa łączność
Android 8.0 (poziom interfejsu API 26) wprowadza szereg ulepszeń ustawień Wi-Fi, aby ułatwić wybór sieci Wi-Fi, która zapewnia najlepsze wrażenia użytkownika. Dotychczasowe zmiany:
- Ulepszenia stabilności i niezawodności.
- bardziej czytelny interfejs;
- jedno, skonsolidowane menu Ustawienia Wi-Fi.
- Na zgodnych urządzeniach automatyczne włączanie Wi-Fi, gdy w pobliżu znajduje się zapisana sieć wysokiej jakości.
Bezpieczeństwo
W Androidzie 8.0 wprowadziliśmy te zmiany dotyczące bezpieczeństwa:
- Platforma nie obsługuje już protokołu SSLv3.
- Podczas nawiązywania połączenia HTTPS z serwerem, który nieprawidłowo implementuje negocjację wersji protokołu TLS,
HttpsURLConnection
nie próbuje już obejścia problemu przez przejście na wcześniejsze wersje protokołu TLS i ponowne próby. - Android 8.0 (poziom API 26) stosuje filtr SECCOMP do wszystkich aplikacji. Lista dozwolonych wywołań systemowych jest ograniczona do tych, które są dostępne za pomocą bionic. Chociaż istnieje kilka innych wywołań systemowych udostępnionych ze względu na zgodność z wcześniejszymi wersjami, zalecamy, aby ich nie używać.
- Obiekty
WebView
w aplikacji są teraz uruchamiane w trybie wieloprocesowym. Treści internetowe są obsługiwane w ramach osobnego, odizolowanego procesu od procesu aplikacji, aby zwiększyć bezpieczeństwo. -
Nie możesz już zakładać, że pliki APK znajdują się w katalogach, których nazwy kończą się na -1 lub -2. Aplikacje powinny używać
sourceDir
do uzyskiwania danych z katalogu, a nie bezpośrednio formatu katalogu. - Informacje o ulepszeniach zabezpieczeń związanych z korzystaniem z bibliotek natywnych znajdziesz w artykule Biblioteki natywne.
Dodatkowo Android 8.0 (interfejs API na poziomie 26) wprowadza te zmiany dotyczące instalowania nieznanych aplikacji z nieznanych źródeł:
- Wartość starszego ustawienia
INSTALL_NON_MARKET_APPS
zawsze wynosi 1. Aby określić, czy nieznane źródło może instalować aplikacje za pomocą instalatora pakietu, należy użyć zamiast tego wartości zwracanej przez funkcjęcanRequestPackageInstalls()
. - Jeśli spróbujesz zmienić wartość
INSTALL_NON_MARKET_APPS
za pomocąsetSecureSetting()
, zostanie zgłoszonyUnsupportedOperationException
. Aby uniemożliwić użytkownikom instalowanie nieznanych aplikacji z nieznanych źródeł, zastosuj ograniczenie dotyczące użytkowników (DISALLOW_INSTALL_UNKNOWN_SOURCES
). -
Profile zarządzane utworzone na urządzeniach z Androidem 8.0 (poziom interfejsu API 26) mają automatycznie włączone ograniczenia dotyczące użytkownika
DISALLOW_INSTALL_UNKNOWN_SOURCES
. W przypadku istniejących profili zarządzanych na urządzeniach z Androidem 8.0 włączona jest automatycznie opcjaDISALLOW_INSTALL_UNKNOWN_SOURCES
, chyba że właściciel profilu wyraźnie ją wyłączył (przed uaktualnieniem) przez ustawienie wartościINSTALL_NON_MARKET_APPS
na 1.
Więcej informacji o instalowaniu nieznanych aplikacji znajdziesz w poradnikach dotyczących uprawnień do instalowania nieznanych aplikacji.
Dodatkowe wskazówki na temat zwiększania bezpieczeństwa aplikacji znajdziesz w artykule Bezpieczeństwo dla deweloperów aplikacji na Androida.
Prywatność
Android 8.0 (poziom 26 interfejsu API) wprowadza na platformie te zmiany związane z prywatnością:
- Platforma obsługuje teraz identyfikatory inaczej.
-
W przypadku aplikacji zainstalowanych przed aktualizacją OTA do wersji Androida 8.0 (poziom interfejsu API 26) (poziom interfejsu API 26) wartość
ANDROID_ID
pozostaje bez zmian, chyba że zostanie odinstalowana, a potem ponownie zainstalowana po aktualizacji OTA. Aby zachować wartości po odinstalowaniu OTA, deweloperzy mogą powiązać stare i nowe wartości, używając kopii zapasowej kluczy i wartości. - W przypadku aplikacji zainstalowanych na urządzeniu z Androidem 8.0 wartość parametru
ANDROID_ID
jest teraz ograniczona do klucza podpisywania aplikacji i użytkownika. WartośćANDROID_ID
jest unikalna dla każdej kombinacji klucza podpisywania aplikacji, użytkownika i urządzenia. W efekcie aplikacje z różnymi kluczami podpisywania działające na tym samym urządzeniu nie będą już widzieć tego samego identyfikatora Android ID (nawet w przypadku tego samego użytkownika). - Wartość
ANDROID_ID
nie zmienia się po odinstalowaniu lub ponownym zainstalowaniu pakietu, o ile klucz podpisywania pozostaje ten sam (i aplikacja nie została zainstalowana przed aktualizacją OTA do wersji Androida 8.0). - Wartość
ANDROID_ID
nie zmienia się nawet wtedy, gdy aktualizacja systemu powoduje zmianę klucza podpisywania pakietu. - Na urządzeniach z usługami Google Play i identyfikatorem wyświetlania reklam musisz używać
identyfikatora wyświetlania reklam. Prosty, standardowy system zarabiania na aplikacjach. Identyfikator wyświetlania reklam to unikalny, możliwy do zresetowania przez użytkownika identyfikator wyświetlania reklam. Jest ona udostępniana przez Usługi Google Play.
Inni producenci urządzeń powinni nadal udostępniać
ANDROID_ID
.
-
W przypadku aplikacji zainstalowanych przed aktualizacją OTA do wersji Androida 8.0 (poziom interfejsu API 26) (poziom interfejsu API 26) wartość
- Zapytanie do właściwości systemowej
net.hostname
zwraca wartość null.
Logowanie niewykrytych wyjątków
Jeśli aplikacja zainstaluje Thread.UncaughtExceptionHandler
, która nie wywołuje domyślnej Thread.UncaughtExceptionHandler
, system nie zabija aplikacji, gdy wystąpi nieprzechwycony wyjątek. Począwszy od Androida 8.0 (poziom interfejsu API 26) system w tej sytuacji rejestruje wyjątek stosu wyjątków. We wcześniejszych wersjach platformy system nie zarejestrował tego zrzutu stosu wyjątków.
Zalecamy, aby niestandardowe implementacje Thread.UncaughtExceptionHandler
zawsze wywoływały domyślny moduł obsługi. Zmiana w Androidzie 8.0 nie ma wpływu na aplikacje, które korzystają z tego zalecenia.
Zmiana podpisu findViewById()
Wszystkie instancje metody findViewById()
zwracają teraz
<T extends View> T
zamiast View
. Ta zmiana będzie miała te konsekwencje:
- Może to spowodować, że istniejący kod będzie miał niejednoznaczny typ zwracanych danych, na przykład jeśli istnieją funkcje
someMethod(View)
isomeMethod(TextView)
, które przyjmują wynik wywołania funkcjifindViewById()
. - W przypadku języka źródłowego Java 8 wymagane jest jawne zastąpienie wartości
View
, gdy typ zwracany nie jest ograniczony (np.assertNotNull(findViewById(...)).someViewMethod())
). - Zastąpienia metod
findViewById()
, które nie są metodami końcowymi (na przykładActivity.findViewById()
), będą wymagać zaktualizowania typu zwracanej wartości.
Zmiana statystyk użytkowania dostawcy kontaktów
W poprzednich wersjach Androida komponent Dostawca kontaktów umożliwia programistom uzyskanie danych o korzystaniu z każdego kontaktu. Te dane o korzystaniu zawierają informacje o każdym adresie e-mail i numerze telefonu powiązanym z kontaktem, w tym liczbę i czas ostatniego kontaktu z kontaktem. Aplikacje, które wymagają uprawnienia
READ_CONTACTS
mogą odczytywać te dane.
Aplikacje mogą nadal odczytywać te dane, jeśli poproszą o uprawnienia READ_CONTACTS
. W Androidzie 8.0 (poziom interfejsu API 26) i nowszych wersjach zapytania dotyczące danych o użytkowaniu zwracają wartości przybliżone, a nie dokładne. System Androida przechowuje dokładne wartości wewnętrznie, więc ta zmiana nie ma wpływu na interfejs Autocomplete API.
Ta zmiana działania wpływa na te parametry zapytania:
Obsługa odbioru
AbstractCollection.removeAll()
i AbstractCollection.retainAll()
teraz zawsze rzucają błąd NullPointerException
; wcześniej błąd NullPointerException
nie był rzucany, gdy kolekcja była pusta. Ta zmiana sprawia, że działanie jest zgodne z dokumentacją.
Android Enterprise
Android 8.0 (poziom interfejsu API 26) zmienia zachowanie niektórych interfejsów API i funkcji w aplikacjach dla firm, w tym kontrolerów zasad urządzeń (DPC). Zmiany obejmują:
- Nowe zachowania, które pomagają aplikacjom obsługiwać profile służbowe na urządzeniach w pełni zarządzanych.
- Zmiany w obsługiwaniu aktualizacji systemu, weryfikacji aplikacji i uwierzytelnianiu w celu zwiększenia integralności urządzenia i systemu.
- Ulepszenia wrażeń użytkowników dotyczącego obsługi, powiadomień, ekranu Ostatnie i VPN-u zawsze włączonego.
Aby zobaczyć wszystkie zmiany w Androidzie 8.0 (interfejs API na poziomie 26) dotyczące przedsiębiorstw i dowiedz się, jak mogą one wpłynąć na Twoją aplikację, przeczytaj artykuł Android w przedsiębiorstwie.
Aplikacje kierowane na Androida 8.0
Te zmiany w działaniu dotyczą tylko aplikacji kierowanych na Androida 8.0 (poziom interfejsu API 26) lub nowszego. Aplikacje skompilowane pod kątem Androida 8.0 lub z ustawionymi parametrami targetSdkVersion
Androida 8.0 lub nowszego muszą zostać zmodyfikowane, aby prawidłowo obsługiwać te zachowania (w odpowiednich przypadkach).
Okna alertów
Aplikacje, które używają uprawnienia SYSTEM_ALERT_WINDOW
, nie mogą już używać tych typów okien do wyświetlania okien alertów nad innymi aplikacjami i oknami systemowymi:
Zamiast tego aplikacje muszą używać nowego typu okna o nazwie TYPE_APPLICATION_OVERLAY
.
Jeśli używasz okna typu TYPE_APPLICATION_OVERLAY
do wyświetlania ostrzeżeń w aplikacji, pamiętaj o tych cechach nowego typu okna:
- Okna alertów aplikacji zawsze pojawiają się pod oknami krytycznymi systemu, takimi jak pasek stanu czy IME.
- Aby usprawnić prezentację ekranu, system może przenosić lub zmieniać rozmiar okien w typie okna
TYPE_APPLICATION_OVERLAY
. - Po otwarciu obszaru powiadomień użytkownicy mogą uzyskać dostęp do ustawień, aby zablokować wyświetlanie okien alertów przez aplikację za pomocą typu okna
TYPE_APPLICATION_OVERLAY
.
Powiadomienia o zmianach treści
Android 8.0 (poziom interfejsu API 26) zmienia sposób działania funkcji ContentResolver.notifyChange()
i registerContentObserver(Uri, boolean, ContentObserver)
w aplikacjach kierowanych na Androida 8.0.
Te interfejsy API wymagają teraz, aby w przypadku wszystkich adresów URI autorytetu zdefiniowano prawidłową wartość parametru ContentProvider
. Zdefiniowanie prawidłowego ContentProvider
z odpowiednimi uprawnieniami pomoże Ci chronić aplikację przed zmianami treści w złośliwych aplikacjach i uniknąć wycieku potencjalnie prywatnych danych do szkodliwych aplikacji.
Wyświetlanie skupienia
Domyślnie możliwe do kliknięcia obiekty View
są również możliwe do zaznaczenia. Jeśli chcesz, aby obiekt View
był klikalny, ale nie można było go zaznaczyć, ustaw atrybut
android:focusable
na false
w pliku XML układu zawierającym element View
lub prześlij wartość false
do elementu setFocusable()
w logice interfejsu użytkownika aplikacji.
Dopasowywanie klienta użytkownika w wykrywaniu przeglądarki
Android 8.0 (poziom interfejsu API 26) i nowsze zawierają ciąg identyfikatora kompilacji OPR
. Niektóre dopasowania wzorców mogą powodować, że logika wykrywania przeglądarki błędnie zidentyfikuje przeglądarkę inną niż Opera jako Operę.
Przykładem takiego dopasowania do wzorca może być:
if(p.match(/OPR/)){k="Opera";c=p.match(/OPR\/(\d+.\d+)/);n=new Ext.Version(c[1])}
Aby uniknąć problemów wynikających z takiej błędnej identyfikacji, użyj w przypadku przeglądarki Opera ciągu innego niż OPR
jako dopasowania do wzorca.
Bezpieczeństwo
Te zmiany wpływają na bezpieczeństwo w Androidzie 8.0 (poziom interfejsu API 26):
- Jeśli konfiguracja bezpieczeństwa sieci w aplikacji wyłącza obsługę ruchu nieszyfrowanego, obiekty
WebView
w aplikacji nie mogą uzyskiwać dostępu do witryn przez HTTP. Każdy obiektWebView
musi używać protokołu HTTPS. - Usunęliśmy ustawienie systemowe Zezwalaj na nieznane źródła. Zamiast niego uprawnienie Instalowanie nieznanych aplikacji służy do zarządzania nieznanymi instalacjami aplikacji z nieznanych źródeł. Więcej informacji o tych nowych uprawnieniach znajdziesz w przewodniku na temat nieznanych uprawnień do instalowania aplikacji.
Więcej wskazówek na temat zwiększania bezpieczeństwa aplikacji znajdziesz w artykule Zabezpieczenia dla deweloperów aplikacji na Androida.
Dostęp do konta i wykrywalność
W Androidzie 8.0 (poziom interfejsu API 26) aplikacje nie mogą już uzyskiwać dostępu do kont użytkowników, chyba że uwierzytelniacz jest ich właścicielem lub użytkownik przyznał dostęp. Uprawnienie GET_ACCOUNTS
nie wystarcza już do zalogowania się. Aby uzyskać dostęp do konta, aplikacje powinny używać AccountManager.newChooseAccountIntent()
lub metody konkretnej dla uwierzytelniania. Po uzyskaniu dostępu do kont aplikacja może wywołać funkcję AccountManager.getAccounts()
, aby z nich skorzystać.
Wycofujemy Androida 8.0
LOGIN_ACCOUNTS_CHANGED_ACTION
. Zamiast tego aplikacje powinny używać interfejsu addOnAccountsUpdatedListener()
, aby otrzymywać informacje o kontach w czasie działania.
Informacje o nowych interfejsach API i metodach dodanych do konta i wykrywalności konta znajdziesz w artykule Dostęp do konta i wykrywalność w sekcji Nowe interfejsy API w tym dokumencie.
Prywatność
Następujące zmiany wpływają na ochronę prywatności w Androidzie 8.0 (poziom interfejsu API 26).
-
Właściwości systemowe
net.dns1
,net.dns2
,net.dns3
inet.dns4
nie są już dostępne. To zmiana poprawiająca ochronę prywatności na platformie. -
Aby uzyskać informacje o sieci, takie jak serwery DNS, aplikacje z uprawnieniem
ACCESS_NETWORK_STATE
mogą zarejestrować obiektNetworkRequest
lubNetworkCallback
. Te klasy są dostępne w Androidzie 5.0 (poziom interfejsu API 21) i nowszych. -
Parametr Build.SERIAL został wycofany.
Aplikacje, które muszą znać numer seryjny sprzętu, powinny zamiast tego używać nowej metody
Build.getSerial()
, która wymaga uprawnieniaREAD_PHONE_STATE
. -
Interfejs API
LauncherApps
nie zezwala już aplikacjom profilu służbowego na dostęp do informacji o profilu głównym. Gdy użytkownik znajduje się w profilu służbowym, interfejs APILauncherApps
zachowuje się tak, jakby żadne aplikacje nie zostały zainstalowane w innych profilach w tej samej grupie profili. Jak wcześniej, próby uzyskania dostępu do niezwiązanych profili powodują SecurityExceptions.
Uprawnienia
W Androidzie 8.0 (poziom interfejsu API 26) w przypadku, gdy aplikacja poprosiła o uprawnienia w czasie działania i zostały one przyznane, system nieprawidłowo przyznawał aplikacji także pozostałe uprawnienia należące do tej samej grupy uprawnień, które były zarejestrowane w pliku manifestu.
W przypadku aplikacji kierowanych na Androida 8.0 to zachowanie zostało poprawione. Aplikacja otrzymuje tylko te uprawnienia, o które wyraźnie prosi. Gdy jednak użytkownik przyzna aplikacji uprawnienia, wszystkie kolejne prośby o uprawnienia z tej grupy uprawnień będą automatycznie przyznawane.
Załóżmy na przykład, że aplikacja zawiera w swoim pliku manifestu zarówno identyfikator READ_EXTERNAL_STORAGE
, jak i WRITE_EXTERNAL_STORAGE
.
Aplikacja prosi o dostęp do READ_EXTERNAL_STORAGE
, a użytkownik zezwala na to. Jeśli aplikacja jest kierowana na interfejs API na poziomie 25 lub niższym, system jednocześnie przyznaje też WRITE_EXTERNAL_STORAGE
, ponieważ należy ona do tej samej grupy uprawnień STORAGE
i jest też zarejestrowana w pliku manifestu. Jeśli aplikacja jest kierowana na Androida 8.0 (poziom interfejsu API 26), system przyznaje jej tylko uprawnienie READ_EXTERNAL_STORAGE
. Jeśli jednak aplikacja poprosi o uprawnienie WRITE_EXTERNAL_STORAGE
, system natychmiast przyzna je bez pytania użytkownika.
Multimedia
- Platforma może samodzielnie przeprowadzać automatyczne wyciszanie dźwięku. W tym przypadku, gdy inna aplikacja żąda fokusa na kluczu
AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
, wybrana aplikacja zmniejsza głośność, ale zwykle nie otrzymuje wywołania zwrotnegoonAudioFocusChange()
i nie traci aktywności audio. W przypadku aplikacji, które muszą wstrzymywać, a nie wyciszać dźwięk, dostępne są nowe interfejsy API, które umożliwiają pominięcie tego zachowania. - Gdy użytkownik odbierze połączenie telefoniczne, aktywne strumienie multimediów będą wyciszone na czas rozmowy.
- Aby opisać odtwarzanie dźwięku, należy użyć interfejsu API
AudioAttributes
, a nie typów strumieni audio. Nadal używaj typów strumieni audio tylko do sterowania głośnością. Inne zastosowania typów strumieni nadal działają (np. argumentstreamType
w przypadku przestarzałego konstruktoraAudioTrack
), ale system rejestruje to jako błąd. - Jeśli podczas korzystania z
AudioTrack
aplikacja poprosi o wystarczająco duży bufor audio, framework spróbuje użyć wyjścia z głębokim buforem, jeśli jest ono dostępne. - W Androidzie 8.0 (poziom interfejsu API 26) obsługa zdarzeń przycisku multimedialnego jest inna:
- Sposób obsługi przycisków multimediów w działaniach interfejsu użytkownika nie uległ zmianie: działania na pierwszym planie nadal mają priorytet.
- Jeśli działanie na pierwszym planie nie obsługuje zdarzenia przycisku multimediów, system przekierowuje to zdarzenie do aplikacji, w której ostatnio odtwarzany jest dźwięk. Przy określaniu, która aplikacja ma otrzymywać zdarzenia przycisku multimediów, nie są brane pod uwagę stan aktywny, flagi ani stan odtwarzania sesji multimediów.
- Jeśli sesja multimediów w aplikacji została opublikowana, system wysyła zdarzenie przycisku multimediów do obiektu
MediaButtonReceiver
aplikacji (jeśli go ma). - W każdym innym przypadku system odrzuca zdarzenie związane z przyciskiem multimediów.
Biblioteki natywne
W aplikacjach kierowanych na Androida 8.0 (poziom interfejsu API 26) natywne biblioteki nie są już wczytywane, jeśli zawierają segment ładowania, który jest jednocześnie zapisywalny i wykonalny. Z powodu tej zmiany niektóre aplikacje mogą przestać działać, jeśli mają biblioteki natywne z nieprawidłowymi segmentami wczytywania. Jest to środek zwiększający bezpieczeństwo.
Więcej informacji znajdziesz w artykule Segmenty możliwe do zapisu i wykonywalne.
Zmiany w linkowaniu są powiązane z poziomem interfejsu API, na który kierowana jest aplikacja. Jeśli nastąpi zmiana tagu łączącego na docelowym poziomie interfejsu API, aplikacja nie będzie mogła wczytać biblioteki. Jeśli kierujesz się na poziom interfejsu API niższy niż ten, na którym występuje zmiana w linkerze, logcat wyświetla ostrzeżenie.
Obsługa odbioru
W Androidzie 8.0 (poziom interfejsu API 26) zamiast List.sort()
zaimplementowano interfejs Collections.sort()
. W Androidzie 7.x (poziom interfejsu API 24 i 25) było to odwrotne: domyślna implementacja List.sort()
nazywała się Collections.sort()
.
Ta zmiana pozwala Collections.sort()
korzystać z optymalnych implementacji List.sort()
, ale wiąże się z tymi ograniczeniami:
Implementacje funkcji
List.sort()
nie mogą wywoływać funkcjiCollections.sort()
, ponieważ spowodowałoby to przepełnienie stosu z powodu nieskończonej rekurencji. Jeśli chcesz, aby implementacjaList
działała domyślnie, unikaj zastępowania właściwościsort()
.Jeśli klasa nadrzędna implementuje funkcję
sort()
w nieodpowiedni sposób, zwykle można zastąpić funkcjęList.sort()
implementacją opartą na funkcjachList.toArray()
,Arrays.sort()
iListIterator.set()
. Na przykład:@Override public void sort(Comparator<? super E> c) { Object[] elements = toArray(); Arrays.sort(elements, c); ListIterator<E> iterator = (ListIterator<Object>) listIterator(); for (Object element : elements) { iterator.next(); iterator.set((E) element); } }
W większości przypadków możesz też zastąpić
List.sort()
implementację implementacją, która deleguje do różnych domyślnych implementacji w zależności od poziomu interfejsu API. Na przykład:@Override public void sort(Comparator<? super E> comparator) { if (Build.VERSION.SDK_INT <= 25) { Collections.sort(this); } else { super.sort(comparator); } }
Jeśli robisz to tylko dlatego, że chcesz mieć metodę
sort()
dostępną na wszystkich poziomach interfejsu API, rozważ nadanie jej unikalnej nazwy, np.sortCompat()
, zamiast zastępować metodęsort()
.-
Collections.sort()
jest teraz uznawana za modyfikację strukturalną w implementacjach list, które wywołująsort()
. Na przykład w wersjach platformy starszych niż Android 8.0 (poziom interfejsu API 26) iteracja z użyciem parametruArrayList
i wywołanie w niej funkcjisort()
w trakcie iteracji skutkowało wywołaniemConcurrentModificationException
, jeśli sortowanie zostało wykonane przez wywołanieList.sort()
.Collections.sort()
nie zwrócił wyjątku.Ta zmiana powoduje, że działanie platformy jest bardziej spójne:
ConcurrentModificationException
.
zachowanie podczas wczytywania zajęć,
Android 8.0 (poziom interfejsu API 26) sprawdza, czy ładowarki klas nie naruszają założeń środowiska uruchomieniowego podczas wczytywania nowych klas. Te kontrole są wykonywane niezależnie od tego, czy klasa jest wywoływana z Java (z forName()
), kodu bajtowego Dalvik czy JNI. Platforma nie przechwytuje bezpośrednich wywołań z Java do metody loadClass()
ani nie sprawdza wyników takich wywołań. Takie działanie nie powinno wpływać na działanie dobrze zachowujących się ładowaczy klas.
Platforma sprawdza, czy deskryptor klasy zwracany przez moduł ładowania klas pasuje do oczekiwanego deskryptora. Jeśli zwrócony deskryptor nie jest zgodny, platforma zgłasza błąd NoClassDefFoundError
, a w wyjątku zapisuje szczegółowy komunikat informujący o rozbieżnościach.
Platforma sprawdza również, czy deskryptory żądanych klas są prawidłowe. Ta weryfikacja wykrywa wywołania JNI, które pośrednio wczytują klasy takie jak GetFieldID()
, przekazując nieprawidłowe deskryptory do tych klas. Na przykład pole z podpisem java/lang/String
nie zostało znalezione, ponieważ podpis jest nieprawidłowy. Powinno być Ljava/lang/String;
.
Jest to inne niż wywołanie JNI do funkcji FindClass()
, gdzie java/lang/String
jest prawidłową pełną nazwą.
Android 8.0 (poziom interfejsu API 26) nie pozwala na definiowanie klas przez wiele modułów ładowania klas przy użyciu tego samego obiektu DexFile. Podczas próby wykonania tej operacji środowisko uruchomieniowe Androida InternalError
wyświetla błąd z komunikatem „Próba zarejestrowania pliku dex <filename>
z wieloma ładowarkami klas”.
Interfejs DexFile API został wycofany. Zdecydowanie zalecamy korzystanie z jednego z modułów ładowania klas platformy, w tym PathClassLoader
lub BaseDexClassLoader
.
Uwaga: możesz utworzyć większą liczbę ładowarek klas, które odwołują się do tego samego kontenera pliku APK lub JAR w systemie plików. Zazwyczaj nie powoduje to dużego obciążenia pamięci. Jeśli pliki DEX w kontenerze są przechowywane zamiast skompresowane, platforma może wykonać na nich operację mmap
, zamiast wyodrębniać je bezpośrednio. Jeśli jednak platforma musi wyodrębnić plik DEX z kontenera, odwoływanie się do tego pliku w ten sposób może zużywać dużo pamięci.
W Androidzie wszystkie ładowarki klas są uważane za zdolne do działania równoległego. Gdy kilka wątków rywalizuje o załadowanie tego samego zbioru za pomocą tego samego ładowarki klas, wygrywa pierwszy wątek, który ukończył operację, a jego wynik jest używany przez pozostałe wątki. Takie zachowanie występuje niezależnie od tego, czy ładownik klasy zwrócił tę samą klasę, inną klasę czy wyjątek. Platforma dyskretnie ignoruje takie wyjątki.
Uwaga: w wersjach platformy starszych niż Android 8.0 (poziom interfejsu API 26) naruszenie tych założeń może spowodować wielokrotne zdefiniowanie tej samej klasy, uszkodzenie stosu z powodu pomylenia klas i innych niepożądanych efektów.