Poziom API: 17
Android 4.2 (JELLY_BEAN_MR1
) to aktualizacja wersji Jelly Bean, która udostępnia nowe funkcje dla użytkowników i deweloperów aplikacji. Ten dokument zawiera wprowadzenie do najciekawszych i najbardziej przydatnych nowych interfejsów API dla deweloperów.
Deweloper aplikacji powinien jak najszybciej pobrać obraz systemu Android 4.2 i platformę SDK z Menedżera pakietów SDK. Jeśli nie masz urządzenia z Androidem 4.2, na którym chcesz przetestować aplikację, użyj obrazu systemu Android 4.2, by przetestować aplikację w emulatorze Androida. Następnie skompiluj aplikacje na platformie Androida 4.2, aby zacząć korzystać z najnowszych interfejsów API.
Aby lepiej zoptymalizować aplikację pod kątem urządzeń z Androidem 4.2, ustaw targetSdkVersion
na "17"
, zainstaluj ją na obrazie systemu Androida 4.2, przetestuj ją, a następnie opublikuj aktualizację z tą zmianą.
Możesz używać interfejsów API w Androidzie 4.2, a jednocześnie obsługiwać starsze wersje. Aby to zrobić, dodaj do kodu warunki, które przed wykonaniem interfejsów API nieobsługiwanych przez minSdkVersion
sprawdzają poziom interfejsu API systemu.
Aby dowiedzieć się więcej o zachowywaniu zgodności wstecznej, przeczytaj artykuł Tworzenie interfejsów zgodnych z wsteczną wersją.
Więcej informacji o tym, jak działają poziomy interfejsu API, znajdziesz w artykule Co to jest poziom interfejsu API?.
Ważne zmiany w działaniu
Jeśli masz już opublikowaną aplikację na Androida, pamiętaj o tych zmianach, które mogą wpłynąć na jej działanie:
- Dostawcy treści nie są już domyślnie eksportowani. To oznacza, że domyślna wartość atrybutu
android:exported
to teraz“false"
. Jeśli inne aplikacje mają mieć dostęp do Twojego dostawcy treści, musisz teraz określić ustawienieandroid:exported="true"
.Ta zmiana zostanie zastosowana tylko wtedy, gdy dla parametru
android:targetSdkVersion
lubandroid:minSdkVersion
ustawisz wartość 17 lub więcej. W przeciwnym razie domyślną wartością jest“true"
nawet w Androidzie 4.2 i nowszych. - W porównaniu z poprzednimi wersjami Androida wyniki dotyczące lokalizacji użytkownika mogą być mniej dokładne, jeśli aplikacja prosi o uprawnienie
ACCESS_COARSE_LOCATION
, ale nie prosi o nieACCESS_FINE_LOCATION
.Aby spełnić oczekiwania użytkowników dotyczące ochrony prywatności, gdy Twoja aplikacja prosi o zgodę na przybliżoną lokalizację (a nie dokładną lokalizację), system nie będzie podawać przybliżonej lokalizacji użytkownika, która byłaby dokładniejsza niż w przypadku części miasta.
- Niektóre ustawienia urządzenia zdefiniowane przez
Settings.System
są teraz tylko do odczytu. Jeśli aplikacja spróbuje zapisać zmiany w ustawieniach zdefiniowanych wSettings.System
, które zostały przeniesione doSettings.Global
, operacja zapisu zakończy się niepowodzeniem w Androidzie 4.2 lub nowszym.Nawet jeśli wartość parametrów
android:targetSdkVersion
iandroid:minSdkVersion
jest niższa niż 17, aplikacja na Androidzie 4.2 lub nowszym nie jest w stanie zmienić ustawień, które zostały przeniesione doSettings.Global
. - Jeśli Twoja aplikacja korzysta z języka
WebView
, Android 4.2 zapewnia dodatkową warstwę zabezpieczeń, dzięki której możesz bezpiecznie powiązać JavaScript z kodem Androida. Jeśli ustawisz wartośćtargetSdkVersion
na 17 lub wyższą, musisz teraz dodać adnotację@JavascriptInterface
do dowolnej metody, która ma być dostępna dla JavaScriptu (ta metoda również musi być publiczna). Jeśli nie dodasz adnotacji, metoda nie będzie dostępna dla strony internetowej wWebView
podczas działania na Androidzie 4.2 lub nowszym. Jeśli ustawisztargetSdkVersion
na 16 lub mniej, adnotacja nie jest wymagana, ale zalecamy zaktualizowanie wersji docelowej i dodanie adnotacji, aby zwiększyć bezpieczeństwo.Dowiedz się więcej o wiązaniu kodu JavaScript z kodem Androida.
Daydream
Daydream to nowy, interaktywny tryb wygaszacza ekranu na urządzenia z Androidem. Włącza się automatycznie, gdy urządzenie znajduje się w stacji dokującej lub jest nieaktywne, gdy jest podłączone do ładowarki (zamiast wyłączać ekran). Daydream wyświetla obraz po jednym śnie – może to być czysto wizualny, pasywny wyświetlacz, który znika po dotknięciu lub może być interaktywny i responsywny na pełny zestaw zdarzeń wejściowych. Twoje marzenia spełniają się w aplikacji i mają pełny dostęp do narzędzi interfejsu Androida, w tym widoków, układów i animacji, dzięki czemu są bardziej elastyczne i skuteczniejsze od animowanych tapet czy widżetów aplikacji.
Możesz stworzyć marzenie dla Daydream, implementując podklasę DreamService
. Interfejsy DreamService
API zostały zaprojektowane tak, aby przypominały interfejsy Activity
. Aby określić interfejs użytkownika dla swojego marzenia, możesz przekazać identyfikator zasobu układu lub View
do setContentView()
w dowolnym momencie po utworzeniu okna, np. z wywołania zwrotnego onAttachedToWindow()
.
Klasa DreamService
udostępnia inne ważne metody wywołań zwrotnych cyklu życia oprócz podstawowych interfejsów API Service
, takich jak onDreamingStarted()
, onDreamingStopped()
i onDetachedFromWindow()
.
Nie możesz zainicjować DreamService
w aplikacji – jest ona uruchamiana automatycznie przez system.
Jeśli Twoje marzenie jest interaktywne, możesz z niego uruchomić aktywność, która przekieruje użytkownika do pełnego interfejsu aplikacji, gdzie będzie można uzyskać więcej szczegółów i kontrolę. Za pomocą funkcji finish()
możesz zakończyć sen, aby użytkownik mógł zobaczyć nową aktywność.
Aby udostępnić w systemie wygaszacz ekranu, zadeklaruj DreamService
za pomocą elementu <service>
w pliku manifestu. Następnie do działania "android.service.dreams.DreamService"
musisz dodać filtr intencji. Na przykład:
<service android:name=".MyDream" android:exported="true" android:icon="@drawable/dream_icon" android:label="@string/dream_label" > <intent-filter> <action android:name="android.service.dreams.DreamService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
W DreamService
są też inne przydatne metody, o których warto pamiętać:
setInteractive(boolean)
określa, czy sen odbiera zdarzenia wejściowe lub wyjścia natychmiast po wprowadzeniu przez użytkownika. Jeśli sen jest interaktywny, użytkownik może go wyłączyć, klikając przycisk Wstecz lub Ekran główny. Możesz też użyć poleceniafinish()
, aby je zatrzymać.- Jeśli chcesz wyświetlić pełny obraz, możesz wywołać metodę
setFullscreen()
, by ukryć pasek stanu. - Przed uruchomieniem wygaszacza ekranu ekran przyciemnia się, aby zasygnalizować użytkownikowi, że zbliża się limit czasu bezczynności. Połączenie z numerem
setScreenBright(true)
umożliwia ustawienie zwykłej jasności wyświetlacza.
Więcej informacji znajdziesz w dokumentacji DreamService
.
Wyświetlacze dodatkowe
Android zezwala teraz aplikacji na wyświetlanie unikalnych treści na dodatkowych ekranach połączonych z urządzeniem użytkownika, korzystając z połączenia przewodowego lub Wi-Fi.
Aby utworzyć niepowtarzalną treść na potrzeby dodatkowego wyświetlacza, rozszerz klasę Presentation
i zaimplementuj wywołanie zwrotne onCreate()
. W onCreate()
określ interfejs ekranu dodatkowego, wywołując setContentView()
.
Jako rozszerzenie klasy Dialog
klasa Presentation
określa region, w którym aplikacja może wyświetlać unikalny interfejs na dodatkowym wyświetlaczu.
Aby wykryć wyświetlacze dodatkowe, w których możesz wyświetlić Presentation
, użyj interfejsów API DisplayManager
lub MediaRouter
. Chociaż interfejsy API DisplayManager
umożliwiają wyliczenie wielu wyświetlaczy, które mogą być podłączone jednocześnie, zwykle aby uzyskać szybki dostęp do domyślnego wyświetlacza systemu, należy użyć elementu MediaRouter
.
Aby ustawić domyślny ekran prezentacji, wywołaj funkcję MediaRouter.getSelectedRoute()
i przekaż ją ROUTE_TYPE_LIVE_VIDEO
. Zwraca on obiekt MediaRouter.RouteInfo
, który opisuje trasę aktualnie wybraną przez system w przypadku prezentacji wideo. Jeśli MediaRouter.RouteInfo
nie ma wartości null, wywołaj getPresentationDisplay()
, aby uzyskać parametr Display
reprezentujący połączony wyświetlacz.
Następnie możesz wyświetlić prezentację, przekazując obiekt Display
do konstruktora klasy Presentation
. Prezentacja pojawi się
na ekranie dodatkowym.
Aby w czasie działania wykrywać połączenie z nowym wyświetlaczem, utwórz wystąpienie elementu MediaRouter.SimpleCallback
, w którym wdrożysz metodę wywołania zwrotnego onRoutePresentationDisplayChanged()
, która będzie wywoływana po podłączeniu nowego wyświetlacza do prezentacji. Następnie zarejestruj MediaRouter.SimpleCallback
, przekazując go do MediaRouter.addCallback()
wraz z typem trasy ROUTE_TYPE_LIVE_VIDEO
. Gdy ktoś zadzwoni pod numer onRoutePresentationDisplayChanged()
, po prostu zadzwoń pod numer MediaRouter.getSelectedRoute()
w sposób opisany powyżej.
Aby jeszcze bardziej zoptymalizować interfejs użytkownika Presentation
na potrzeby ekranów dodatkowych, możesz zastosować inny motyw, określając atrybut android:presentationTheme
w <style>
aplikacji lub aktywności.
Pamiętaj, że ekrany podłączone do urządzenia użytkownika często mają większy rozmiar i zapewniają inną gęstość. Charakterystyka ekranu może się różnić, dlatego warto zapewnić
zasoby zoptymalizowane pod kątem dużych wyświetlaczy. Jeśli chcesz zażądać dodatkowych zasobów z Presentation
, wywołaj getContext()
.getResources()
, aby uzyskać obiekt Resources
odpowiadający wyświetlaczowi. W ten sposób uzyskasz zasoby z aplikacji, które najlepiej odpowiadają rozmiarowi i gęstości ekranu dodatkowego wyświetlacza.
Więcej informacji i przykłady kodu znajdziesz w dokumentacji klasy Presentation
.
Widżety na ekranie blokady
Android pozwala teraz użytkownikom dodawać widżety aplikacji do ekranu blokady. Aby udostępnić widżet aplikacji na ekranie blokady, dodaj do pliku XML atrybut android:widgetCategory
, który określa parametr AppWidgetProviderInfo
. Ten atrybut obsługuje 2 wartości: home_screen
i keyguard
. Domyślnie atrybut ma wartość home_screen
, aby użytkownicy mogli dodać widżet aplikacji do ekranu głównego. Jeśli chcesz, aby widżet aplikacji był też dostępny na ekranie blokady, dodaj wartość keyguard
:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:widgetCategory="keyguard|home_screen"> </appwidget-provider>
Za pomocą atrybutu android:initialKeyguardLayout
określ też początkowy układ widżetu aplikacji na ekranie blokady. Działa to tak samo jak android:initialLayout
, ponieważ zapewnia układ, który może pojawiać się od razu do momentu zainicjowania widżetu aplikacji, który będzie mógł zaktualizować układ.
Więcej informacji o tworzeniu widżetów aplikacji na ekran blokady, w tym o prawidłowym rozmiarze widżetu aplikacji na ekranie blokady, znajdziesz w przewodniku Widżety aplikacji.
Wielu użytkowników
Android pozwala teraz na korzystanie z wielu przestrzeni użytkowników na współdzielonych urządzeniach, np. tabletach. Każdy użytkownik urządzenia ma własny zestaw kont, aplikacji, ustawień systemu, plików i innych powiązanych z nim danych.
Deweloper nie musi nic więcej robić, aby jego aplikacja działała prawidłowo z wieloma użytkownikami na jednym urządzeniu. Niezależnie od liczby użytkowników na urządzeniu dane zapisywane przez aplikację dla danego użytkownika są przechowywane oddzielnie od danych, które aplikacja zapisuje dla innych użytkowników. System śledzi, które dane użytkownika należą do procesu, w którym działa aplikacja, i zapewnia aplikacji dostęp tylko do danych tego użytkownika, ale nie zezwala na dostęp do danych innych użytkowników.
Zapisywanie danych w środowisku, z którego korzysta wielu użytkowników
Gdy aplikacja zapisuje ustawienia użytkownika, tworzy bazę danych lub zapisuje plik w pamięci wewnętrznej lub zewnętrznej użytkownika, dane te są dostępne tylko wtedy, gdy użytkownik działa jako ten użytkownik.
Aby mieć pewność, że aplikacja będzie działać prawidłowo w środowisku z wieloma użytkownikami, nie odwołuj się do wewnętrznego katalogu aplikacji ani do lokalizacji pamięci zewnętrznej za pomocą zakodowanych na stałe ścieżek. Zamiast tego zawsze używaj odpowiednich interfejsów API:
- Aby uzyskać dostęp do pamięci wewnętrznej, użyj
getFilesDir()
,getCacheDir()
lubopenFileOutput()
. - Aby uzyskać dostęp do pamięci zewnętrznej, użyj
getExternalFilesDir()
lubgetExternalStoragePublicDirectory()
.
Niezależnie od tego, którego z tych interfejsów API używasz do zapisywania danych danego użytkownika, nie będą one dostępne, gdy zostanie uruchomiony jako inny użytkownik. Z perspektywy Twojej aplikacji każdy użytkownik działa na zupełnie innym urządzeniu.
Identyfikowanie użytkowników w środowisku, w którym korzysta wielu użytkowników
Jeśli Twoja aplikacja ma identyfikować unikalnych użytkowników, np. aby zbierać dane analityczne lub tworzyć inne powiązania kont, zastosuj się do zalecanych metod identyfikowania unikalnych instalacji. Tworząc nowy obiekt UUID
przy pierwszym uruchomieniu aplikacji, możesz uzyskać unikalny identyfikator do śledzenia każdego użytkownika, niezależnie od tego, ilu użytkowników zainstalowało Twoją aplikację na jednym urządzeniu. Możesz też zapisać token lokalny pobrany z serwera lub użyć identyfikatora rejestracji podanego przez Google Cloud Messaging.
Pamiętaj, że jeśli aplikacja żąda jednego z identyfikatorów urządzenia sprzętowych (np. adresu MAC sieci Wi-Fi lub numeru SERIAL
), będzie ona podawać tę samą wartość dla każdego użytkownika, ponieważ identyfikatory te są powiązane ze sprzętem, a nie użytkownikiem. Nie wspominając o innych problemach, jakie te identyfikatory wywołują, omówione w poście na blogu Identyfikowanie instalacji aplikacji.
Nowe ustawienia globalne
Ustawienia systemowe zostały zaktualizowane, aby obsługiwały wielu użytkowników. Dodaliśmy teraz Settings.Global
. Ta kolekcja ustawień jest podobna do ustawień Settings.Secure
, ponieważ są one tylko do odczytu, ale są stosowane globalnie we wszystkich przestrzeniach użytkownika na urządzeniu.
Kilka dotychczasowych ustawień zostało przeniesionych tutaj z Settings.System
lub Settings.Secure
. Jeśli Twoja aplikacja wprowadza obecnie zmiany w ustawieniach, które zostały wcześniej zdefiniowane w Settings.System
(np. AIRPLANE_MODE_ON
), po przeniesieniu tych ustawień do Settings.Global
nie będzie to już działać na urządzeniach z Androidem 4.2 lub nowszym. Możesz nadal odczytywać ustawienia w usłudze Settings.Global
, ale ponieważ ich zmiana w aplikacjach nie jest już uważana za bezpieczną, próba zmiany hasła zakończy się niepowodzeniem, a gdy uruchomisz aplikację na Androidzie 4.2 lub nowszym, system zapisze ostrzeżenie w dzienniku systemowym.
Obsługa układu od prawej do lewej
Android oferuje teraz kilka interfejsów API, które pozwalają tworzyć interfejsy, które płynnie zmieniają orientację układu pod kątem obsługi języków korzystających z interfejsów użytkownika od prawej do lewej (RTL) i kierunku czytania, takich jak arabski i hebrajski.
Aby rozpocząć obsługę układów RTL w aplikacji, ustaw atrybut android:supportsRtl
na element <application>
w pliku manifestu i ustaw go na “true"
. Gdy to zrobisz, system włączy różne interfejsy API RTL, aby wyświetlać w aplikacji układy RTL. Na przykład na pasku działań po prawej stronie będzie widoczna ikona i tytuł, a po lewej – przyciski poleceń. Wszystkie układy utworzone za pomocą klas View
udostępnianych przez platformę zostaną cofnięte.
Jeśli chcesz jeszcze bardziej zoptymalizować wygląd aplikacji wyświetlanej w układzie od prawej do lewej, masz do dyspozycji 2 podstawowe poziomy optymalizacji:
- Przekonwertuj właściwości układu zorientowanego na lewo i prawo na właściwości układu z orientacją początkową i końcową.
Na przykład użyj
android:layout_marginStart
zamiastandroid:layout_marginLeft
iandroid:layout_marginEnd
zamiastandroid:layout_marginRight
.Klasa
RelativeLayout
zawiera też odpowiednie atrybuty układu, które zastępują pozycje po lewej i prawej stronie, np. wartośćandroid:layout_alignParentStart
pozwala na zastąpienie właściwościandroid:layout_alignParentLeft
iandroid:layout_toStartOf
zamiastandroid:layout_toLeftOf
. - Aby zapewnić pełną optymalizację układów od prawej do lewej, możesz udostępnić zupełnie osobne pliki z układem, korzystając z kwalifikatora zasobów
ldrtl
(oznaczaldrtl
układ to układ od prawej do lewej}). Możesz na przykład zapisać pliki domyślnego układu wres/layout/
, a układy zoptymalizowane od prawej do lewej – w usłudzeres/layout-ldrtl/
.Kwalifikator
ldrtl
doskonale sprawdza się w przypadku zasobów rysowanych, ponieważ pozwala podawać grafiki w kierunku zgodnym z kierunkiem czytania.
Na platformie dostępne są różne inne interfejsy API do obsługi układów RTL, np. w klasie View
do implementowania odpowiednich działań w widokach niestandardowych i w interfejsie Configuration
do wysyłania zapytań dotyczących bieżącego kierunku układu.
Uwaga: jeśli używasz SQlite i masz tabele lub kolumny, które zawierają tylko liczby, zachowaj ostrożność: użycie String.format(String, Object...)
może spowodować błędy w wyniku konwersji liczb na arabskie odpowiedniki, jeśli na urządzeniu masz ustawiony język arabski.
Musisz użyć String.format(Locale,String,Object...)
, aby mieć pewność, że liczby są przechowywane jako ASCII. Do formatowania liczb użyj też String.format("%d", int)
zamiast String.valueOf(int)
.
Zagnieżdżone fragmenty
Można teraz umieszczać fragmenty we fragmentach. Jest to przydatne w różnych sytuacjach, gdy chcesz umieścić dynamiczne i wielokrotnego użytku komponenty UI w komponencie, który jest dynamiczny i wielokrotnego użytku. Jeśli np. używasz ViewPager
do tworzenia fragmentów, które przesuwają się w lewo i w prawo i zajmują większość miejsca na ekranie, możesz je teraz wstawiać na każdej stronie z fragmentami.
Aby zagnieździć fragment, po prostu wywołaj element getChildFragmentManager()
w elemencie Fragment
, w którym chcesz dodać fragment. Zwraca on wartość FragmentManager
, której możesz używać w zwykły sposób w przypadku aktywności najwyższego poziomu do tworzenia transakcji fragmentowych. Oto przykład kodu, który dodaje fragment z istniejącej klasy Fragment
:
Kotlin
val videoFragment = VideoPlayerFragment() childFragmentManager.beginTransaction().apply { add(R.id.video_fragment, videoFragment) commit() }
Java
Fragment videoFragment = new VideoPlayerFragment(); FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); transaction.add(R.id.video_fragment, videoFragment).commit();
Z poziomu zagnieżdżonego fragmentu możesz uzyskać odwołanie do fragmentu nadrzędnego, wywołując metodę getParentFragment()
.
Biblioteka pomocy Androida obsługuje teraz również zagnieżdżone fragmenty, dzięki czemu możesz implementować zagnieżdżone fragmenty w Androidzie 1.6 i nowszych.
Uwaga: nie można uzupełniać układu do fragmentu, jeśli zawiera on <fragment>
. Zagnieżdżone fragmenty są obsługiwane tylko wtedy, gdy są dodawane do fragmentu dynamicznie.
Skrypt renderowania
Ulepszyliśmy funkcję obliczania w narzędziu Renderscript o te funkcje:
- Wewnętrzne elementy skryptu
Możesz używać wbudowanych skryptów skryptu Renderscript, które implementują typowe operacje za Ciebie, takie jak:
Blends
Blur
Color matrix
3x3 convolve
5x5 convolve
Per-channel lookup table
Converting an Android YUV buffer to RGB
Aby użyć wewnętrznego skryptu, wywołaj statyczną metodę
create()
każdego z zasobów, aby utworzyć instancję skryptu. Następnie możesz wywołać dostępne metodyset()
każdego wewnętrznego skryptu, aby ustawić niezbędne dane wejściowe i opcje. Na koniec wywołaj metodęforEach()
, aby wykonać skrypt.- Grupy skryptów
-
ScriptGroup
umożliwiają łączenie powiązanych skryptów Renderscript i wykonywanie ich za pomocą jednego wywołania.Użyj
ScriptGroup.Builder
, aby dodać wszystkie skrypty do grupy przez wywołanieaddKernel()
. Gdy dodasz wszystkie skrypty, utwórz połączenia między nimi, wywołującaddConnection()
. Po dodaniu połączeń wywołajcreate()
, aby utworzyć grupę skryptów. Przed uruchomieniem grupy skryptów określ dane wejścioweAllocation
i wstępny skrypt do uruchomienia za pomocą metodysetInput(Script.KernelID, Allocation)
oraz podaj dane wyjścioweAllocation
, w których wynik będzie zapisywany i końcowy skrypt do uruchomienia z użyciemsetOutput()
. Na koniec wywołajexecute()
, aby uruchomić grupę skryptów. - Skrypt filtra
-
Filtr SafeSearch definiuje ograniczenia dotyczące istniejących interfejsów API Renderscript, które umożliwiają uruchamianie powstałego kodu na większej liczbie procesorów (procesorów, GPU i DSP). Jeśli chcesz utworzyć pliki Filterscript, utwórz pliki
.fs
zamiast plików.rs
i określ#pragma rs_fp_relaxed
, aby wskazać środowisko wykonawcze Renderscript, że Twoje skrypty nie wymagają ścisłej dokładności punktów zmiennoprzecinkowych IEEE 754-2008. Ta precyzja umożliwia spuszczanie do zera w przypadku denormów i zaokrąglanie do zera. Poza tym skrypty Scriptscript nie mogą korzystać z 32-bitowych typów wbudowanych i muszą określić niestandardową funkcję główną za pomocą atrybutu__attribute__((kernel))
, ponieważ Textscript nie obsługuje wskaźników, które określa domyślny podpis funkcjiroot()
.
Uwaga: chociaż platforma obsługuje filtr Filtrscript, wsparcie dla programistów będzie dostępne w pakiecie SDK w wersji 21.0.1.
Szczegółowy widok wszystkich zmian w interfejsach API w Androidzie 4.2 znajdziesz w raporcie Różnice w interfejsie API.