Interfejsy API Androida 4.2

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ć ustawienie android:exported="true".

    Ta zmiana zostanie zastosowana tylko wtedy, gdy dla parametru android:targetSdkVersion lub android: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 nie ACCESS_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 w Settings.System, które zostały przeniesione do Settings.Global, operacja zapisu zakończy się niepowodzeniem w Androidzie 4.2 lub nowszym.

    Nawet jeśli wartość parametrów android:targetSdkVersion i android:minSdkVersion jest niższa niż 17, aplikacja na Androidzie 4.2 lub nowszym nie jest w stanie zmienić ustawień, które zostały przeniesione do Settings.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 w WebView podczas działania na Androidzie 4.2 lub nowszym. Jeśli ustawisz targetSdkVersion 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ć polecenia finish(), 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:

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:

  1. 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 zamiast android:layout_marginLeft i android:layout_marginEnd zamiast android: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ści android:layout_alignParentLeft i android:layout_toStartOf zamiast android:layout_toLeftOf.

  2. 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 (oznacza ldrtl układ to układ od prawej do lewej}). Możesz na przykład zapisać pliki domyślnego układu w res/layout/, a układy zoptymalizowane od prawej do lewej – w usłudze res/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:

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 metody set() 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łanie addKernel(). Gdy dodasz wszystkie skrypty, utwórz połączenia między nimi, wywołując addConnection(). Po dodaniu połączeń wywołaj create(), aby utworzyć grupę skryptów. Przed uruchomieniem grupy skryptów określ dane wejściowe Allocation i wstępny skrypt do uruchomienia za pomocą metody setInput(Script.KernelID, Allocation) oraz podaj dane wyjściowe Allocation, w których wynik będzie zapisywany i końcowy skrypt do uruchomienia z użyciem setOutput(). Na koniec wywołaj execute(), 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 funkcji root().

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.