Obsługa reklam displayowych

Poniżej znajdziesz informacje o zmianach w obszarach związanych z wyświetlaniem:

Zmień rozmiar aktywności i wyświetlaczy

Aby wskazać, że aplikacja może nie obsługiwać trybu wielu okien ani zmiany rozmiaru, do aktywności użyj atrybutu resizeableActivity=false. Typowe problemy, które występują przy zmianie rozmiaru działań, to między innymi:

  • Działanie może mieć inną konfigurację niż aplikacja lub inny komponent niewizualny. Typowym błędem jest odczytywanie danych wyświetlania z kontekstu aplikacji. Zwracane wartości nie będą dostosowywane do danych dotyczących widocznego obszaru, w którym wyświetlana jest aktywność.
  • Aktywność może nie obsługiwać zmiany rozmiaru i awarii, wyświetlać zniekształcony interfejs lub stracić stan z powodu ponownego uruchomienia bez zapisywania stanu instancji.
  • Aplikacja może próbować używać bezwzględnych współrzędnych wejścia (zamiast tych względnych do pozycji okna), co może spowodować przerwanie wprowadzania danych w trybie wielookiennym.

W Androidzie 7 (lub nowszym) można skonfigurować aplikację resizeableActivity=false tak, aby zawsze uruchamiała się w trybie pełnoekranowym. W takim przypadku platforma uniemożliwia otwieranie działań, których rozmiarów nie można zmieniać, na podzielonym ekranie. Jeśli użytkownik w programie uruchamiającym spróbuje wywołać z Menu z aplikacjami działanie, którego nie można zmienić, w trybie podzielonego ekranu, platforma wyłączy tryb podzielonego ekranu i uruchomi działanie bez możliwości zmiany rozmiaru w trybie pełnoekranowym.

Aplikacje, w których w manifeście ma wartość false, nie mogą być uruchamiane w trybie wielu okien, chyba że zostanie zastosowany tryb zgodności:

  • Do procesu stosowana jest ta sama konfiguracja, która zawiera wszystkie komponenty działań i komponenty niezwiązane z działaniami.
  • Zastosowane ustawienia spełniają wymagania dotyczące wyświetlaczy zgodnych z aplikacją.

W Androidzie 10 platforma nadal uniemożliwia przełączanie się na tryb podzielonego ekranu w przypadku aktywności, których rozmiarów nie można zmieniać, ale można je tymczasowo skalować, jeśli deklarowana jest w nich stała orientacja lub proporcje. W przeciwnym razie rozmiar aktywności jest zmieniany tak, aby wypełniał cały ekran, tak jak w Androidzie 9 i starszych.

Domyślna implementacja stosuje te zasady:

Jeśli aktywność zadeklarowana jako niezgodna z wielozadaniowością za pomocą atrybutu android:resizeableActivity spełnia jedno z podanych niżej warunków, a w przypadku zmiany konfiguracji ekranu zastosowana zostanie oryginalna konfiguracja, a użytkownik otrzyma możliwość ponownego uruchomienia procesu aplikacji, aby użyć zaktualizowanej konfiguracji ekranu.

  • ma stałą orientację dzięki zastosowaniu interfejsu android:screenOrientation;
  • Aplikacja ma domyślny maksymalny lub minimalny współczynnik proporcji przez kierowanie na poziom interfejsu API albo jawnie deklaruje współczynnik proporcji

Ilustracja przedstawiająca aktywność o niezmiennym rozmiarze z deklarowanym współczynnikiem proporcji. Podczas składania urządzenia okno jest skalowane w dół, aby pasowało do obszaru, a format obrazu zostaje zachowany z użyciem odpowiednich czarnych pasów. Dodatkowo po każdej zmianie obszaru wyświetlania aktywności użytkownik otrzymuje opcję ponownego uruchomienia.

Po rozłożeniu urządzenia konfiguracja, rozmiar i format aktywności nie zmieniają się, ale wyświetla się opcja ponownego uruchomienia aktywności.

Jeśli zasada resizeableActivity nie jest skonfigurowana (lub ma wartość true), aplikacja w pełni obsługuje zmianę rozmiaru.

Implementacja

Działanie, którego nie można zmienić, o stałej orientacji lub współczynniku proporcji jest nazywane w kodzie trybem zgodności rozmiaru (SCM). Warunek jest zdefiniowany w ActivityRecord#shouldUseSizeCompatMode(). Gdy uruchamiasz aktywność SCM, konfiguracja związana z ekranem (np. rozmiar lub gęstość) jest ustalana w ramach żądanej konfiguracji zastąpienia, dzięki czemu aktywność nie zależy już od bieżącej konfiguracji wyświetlania.

Jeśli działanie SCM nie może wypełnić całego ekranu, jest wyrównane do góry i wyśrodkowane w poziomie. Granice aktywności są obliczane według wzoru AppWindowToken#calculateCompatBoundsTransformation().

Gdy aktywność SCM używa innej konfiguracji ekranu niż jej kontener (np. rozmiar wyświetlacza jest zmieniony lub aktywność została przeniesiona na inny wyświetlacz), ActivityRecord#inSizeCompatMode() ma wartość prawda, a SizeCompatModeActivityController (w interfejsie System UI) otrzymuje wywołanie zwrotne, aby wyświetlić przycisk ponownego uruchamiania procesu.

Rozmiary i formaty obrazu

Android 10 obsługuje nowe formaty obrazu, od formatów o wysokich proporcjach na długich i wąskich ekranach do formatu 1:1. Aplikacje mogą definiować ApplicationInfo#maxAspectRatioApplicationInfo#minAspectRatio ekranu, które są w stanie obsłużyć.

Formaty aplikacji w Androidzie 10

Rysunek 1. Przykładowe formaty aplikacji obsługiwane na Androidzie 10

Implementacje urządzeń mogą mieć wyświetlacze dodatkowe o rozmiarach i rozdzielczościach mniejszych niż wymagane przez Androida 9 (szerokość lub wysokość 2,5 cala, smallestScreenWidth 320 DP), ale można na nich umieszczać tylko aktywności, które obsługują te małe wyświetlacze.

Aby dołączyć do aplikacji, zadeklaruj minimalny obsługiwany rozmiar, który jest mniejszy niż docelowy rozmiar wyświetlacza lub mu równy. Aby to zrobić, użyj w pliku AndroidManifest atrybutów układu aktywności android:minHeightandroid:minWidth.

Zasady dotyczące reklam displayowych

Android 10 oddziela niektóre zasady dotyczące wyświetlania z domyślnej implementacji WindowManagerPolicyPhoneWindowManager i przenosi je do klas wyświetlania, np.:

  • Stan i obrót wyświetlacza
  • Niektóre klawisze i śledzenie zdarzeń ruchu
  • Interfejs systemu i okna dekoracji

W Androidzie 9 (i starszych wersjach) klasa PhoneWindowManager obsługiwała m.in. zasady wyświetlania, stan i ustawienia, rotację i śledzenie ramek okien dekoracyjnych. Android 10 przenosi większość tych danych do klasy DisplayPolicy z wyjątkiem śledzenia rotacji, które zostało przeniesione do klasy DisplayRotation.

Ustawienia okna wyświetlacza

W Androidzie 10 konfigurowalne ustawienie okna dla poszczególnych wyświetlaczy zostało rozszerzone o:

  • Domyślny tryb wyświetlania okna
  • Wartości wyostrzania
  • Rotacja użytkowników i tryb rotacji
  • Wymuszony rozmiar, gęstość i tryb skalowania
  • Tryb usuwania treści (gdy wyświetlanie jest wyłączone)
  • Obsługa dekoracji systemu i IME

Klasa DisplayWindowSettings zawiera ustawienia tych opcji. Są one zachowywane na dysku na partycji /data w regionie display_settings.xml po każdej zmianie ustawienia. Więcej informacji znajdziesz w DisplayWindowSettings.AtomicFileStorage i DisplayWindowSettings#writeSettings(). Producenci urządzeń mogą podać wartości domyślne w display_settings.xml dla konfiguracji urządzenia. Ponieważ jednak plik jest przechowywany w usłudze /data, jego przywrócenie może wymagać dodatkowych operacji w przypadku usunięcia go przez funkcję czyszczenia.

Domyślnie Android 10 używa DisplayInfo#uniqueId jako identyfikatora wyświetlacza podczas zachowywania ustawień. W przypadku wszystkich wyświetlaczy należy wypełnić pole uniqueId. Ponadto jest stabilny w przypadku wyświetlaczy fizycznych i sieciowych. Identyfikatorem może być też port wyświetlacza fizycznego, który można ustawić w DisplayWindowSettings#mIdentifier. Przy każdym zapisie wszystkie ustawienia są zapisywane, więc można bezpiecznie zaktualizować klucz używany do wpisu dotyczącego wyświetlacza w pamięci. Więcej informacji znajdziesz w artykule Statyczne identyfikatory displayowe.

Ze względów historycznych ustawienia są zachowywane w katalogu /data. Początkowo służyły one do zachowywania ustawień skonfigurowanych przez użytkownika, takich jak rotacja wyświetlacza.

Statyczne identyfikatory displayowe

Android 9 (i starsze) nie zapewniał stabilnych identyfikatorów wyświetlaczy w ramach frameworku. Gdy wyświetlacz został dodany do systemu, dla tego wyświetlacza został wygenerowany identyfikator Display#mDisplayId lub DisplayInfo#displayId poprzez zwiększenie stałego licznika. Jeśli system dodał i usunął ten sam wyświetlacz, pojawiał się inny identyfikator.

Jeśli urządzenie ma kilka wyświetlaczy dostępnych po uruchomieniu, w zależności od czasu mogą one mieć przypisane różne identyfikatory. Chociaż Android 9 (i starsze) uwzględniał DisplayInfo#uniqueId, zabrakło informacji do rozróżniania wyświetlaczy, ponieważ ekrany fizyczne zostały zidentyfikowane jako local:0 lub local:1, aby reprezentować wyświetlacz wbudowany i zewnętrzny.

Android 10 wprowadza zmiany DisplayInfo#uniqueIdw celu dodania stabilnego identyfikatora i wyróżnienia wyświetlaczy lokalnych, sieciowych i wirtualnych.

Typ wyświetlacza Format
Lokalny
local:<stable-id>
Sieć
network:<mac-address>
Wirtualne
virtual:<package-name-and-name>

Oprócz aktualizacji uniqueId, DisplayInfo.address zawiera DisplayAddress, czyli identyfikator wyświetlania, który jest stabilny po ponownym uruchomieniu. W Androidzie 10 DisplayAddress obsługuje ekrany fizyczne i sieciowe. DisplayAddress.Physical zawiera stały identyfikator wyświetlania (taki sam jak uniqueId) i można go utworzyć za pomocą DisplayAddress#fromPhysicalDisplayId().

Android 10 zapewnia też wygodną metodę uzyskiwania informacji o portach (Physical#getPort()). Można jej używać w ramach platformy do statycznego identyfikowania wyświetlaczy. Jest on używany na przykład w DisplayWindowSettings. DisplayAddress.Network zawiera adres MAC i może być tworzony za pomocą DisplayAddress#fromMacAddress().

Te nowości pozwalają producentom urządzeń rozpoznawać wyświetlacze w statycznych konfiguracjach z wieloma wyświetlaczami oraz konfigurować różne ustawienia i funkcje systemu za pomocą statycznych identyfikatorów wyświetlania, takich jak porty wyświetlaczy fizycznych. Te metody są ukryte i są przeznaczone do użytku tylko w system_server.

Na podstawie identyfikatora wyświetlacza HWC (który może być nieprzezroczysty i nie zawsze stabilny) ta metoda zwraca 8-bitowy numer portu (specyficzny dla platformy), który identyfikuje złącze fizyczne wyświetlacza, a także dane EDID wyświetlacza. SurfaceFlinger wyodrębnia informacje o producencie lub modelu z EDID, aby wygenerować stabilne 64-bitowe identyfikatory wyświetlania dostępne dla platformy. Jeśli ta metoda nie jest obsługiwana lub występuje błąd, SurfaceFlinger wraca do starszego trybu MD, w którym DisplayInfo#address to null, a DisplayInfo#uniqueId jest zakodowany na stałe, jak opisano powyżej.

Aby sprawdzić, czy ta funkcja jest obsługiwana, uruchom polecenie:

$ dumpsys SurfaceFlinger --display-id
# Example output.
Display 21691504607621632 (HWC display 0): port=0 pnpId=SHP displayName="LQ123P1JX32"
Display 9834494747159041 (HWC display 2): port=1 pnpId=HWP displayName="HP Z24i"
Display 1886279400700944 (HWC display 1): port=2 pnpId=AUS displayName="ASUS MB16AP"

Korzystanie z więcej niż 2 wyświetlaczy

W Androidzie 9 (i starszych) SurfaceFlinger i DisplayManagerService zakładały istnienie maksymalnie 2 fizycznych wyświetlaczy z zakodowanymi identyfikatorami 0 i 1.

Począwszy od Androida 10 usługa SurfaceFlinger mogła wykorzystać interfejs Hardware Composer (HWC) API do generowania stabilnych identyfikatorów wyświetlania, który umożliwia zarządzanie dowolną liczbą wyświetlaczy fizycznych. Więcej informacji znajdziesz w artykule Identyfikatory statycznych wyświetleń.

Framework może wyszukiwać token IBinder dla fizycznego wyświetlacza za pomocą SurfaceControl#getPhysicalDisplayToken po uzyskaniu 64-bitowego identyfikatora wyświetlacza z SurfaceControl#getPhysicalDisplayIds lub ze zdarzenia DisplayEventReceiver hotplug.

W Androidzie 10 (i starszych) główny wyświetlacz wewnętrzny jest oznaczony jako TYPE_INTERNAL, a wszystkie wyświetlacze dodatkowe jako TYPE_EXTERNAL, niezależnie od typu połączenia. Dlatego dodatkowe ekrany wewnętrzne są traktowane jako zewnętrzne. Aby obejść ten problem, kod na urządzeniu może przyjmować założenia dotyczące DisplayAddress.Physical#getPort, jeśli HWC jest znany, a logika przydziału portów jest przewidywalna.

To ograniczenie zostało usunięte w Androidzie 11 (i nowszych).

  • W Androidzie 11 pierwszy wyświetlacz zgłoszony podczas uruchamiania jest wyświetlaczem głównym. Typ połączenia (wewnętrzne lub zewnętrzne) nie ma znaczenia. Nie można jednak odłączyć wyświetlacza głównego, dlatego w praktyce musi to być wyświetlacz wewnętrzny. Pamiętaj, że niektóre składane telefony mają kilka wewnętrznych ekranów.
  • Wyświetlacze dodatkowe są poprawnie klasyfikowane jako Display.TYPE_INTERNAL lub Display.TYPE_EXTERNAL (wcześniej znane jako Display.TYPE_BUILT_IN i Display.TYPE_HDMI) w zależności od typu połączenia.

Implementacja

W Androidzie 9 i starszych wyświetlacze są identyfikowane za pomocą 32-bitowych identyfikatorów, gdzie 0 to wyświetlacz wewnętrzny, 1 to wyświetlacz zewnętrzny, [2, INT32_MAX] to wirtualne wyświetlacze HWC, a -1 to nieprawidłowy wyświetlacz lub wirtualny wyświetlacz inny niż HWC.

Począwszy od Androida 10 wyświetlacze otrzymują stabilne i trwałe identyfikatory, co umożliwia usługom SurfaceFlinger i DisplayManagerService śledzenie więcej niż 2 wyświetlaczy i rozpoznawanie wcześniej widocznych wyświetlaczy. Jeśli HWC obsługuje IComposerClient.getDisplayIdentificationData i udostępnia dane identyfikacyjne wyświetlacza, SurfaceFlinger analizuje strukturę EDID i przydziela stabilne 64-bitowe identyfikatory wyświetlaczy dla fizycznych i wirtualnych wyświetlaczy HWC. Identyfikatory są wyrażane za pomocą typu opcji, gdzie wartość null reprezentuje nieprawidłowy wyświetlacz lub wirtualny wyświetlacz niebędący wyświetlaczem wysokiej jakości. Bez obsługi HWC SurfaceFlinger korzysta z zachowania zgodnego ze starszymi wersjami z maksymalnie 2 wyświetlaczami fizycznymi.

Cel wyświetlania na poszczególnych wyświetlaczach

Aby obsługiwać kilka źródeł danych wejściowych kierowanych na poszczególne ekrany w tym samym czasie, Androida 10 można skonfigurować tak, aby obsługiwał wiele ukierunkowanych okien, maksymalnie jedno na wyświetlacz. Ta funkcja jest przeznaczona tylko do specjalnych typów urządzeń, gdy wielu użytkowników korzysta z jednego urządzenia w tym samym czasie i każdy z nich używa różnych metod wprowadzania danych lub urządzeń, np. Androida Automotive.

Zdecydowanie zalecamy, aby tej funkcji nie włączać na zwykłych urządzeniach, w tym urządzeniach wieloekranowych ani na komputerach. Jest to spowodowane przede wszystkim względami bezpieczeństwa, które mogą sprawić, że użytkownicy będą się zastanawiać, które okno ma fokus.

Wyobraź sobie, że użytkownik wpisuje bezpieczne informacje w polu do wprowadzania tekstu, na przykład loguje się w aplikacji bankowej lub wpisuje tekst zawierający informacje poufne. Złośliwa aplikacja może utworzyć wirtualny wyświetlacz poza ekranem, na którym będzie wykonywać działanie, a także z polem do wprowadzania tekstu. Prawidłowe i złośliwe działania są wyróżnione i wyświetlają aktywny wskaźnik wprowadzania (migający kursor).

Ponieważ jednak dane wejściowe z klawiatury (sprzętu lub oprogramowania) są rejestrowane tylko w ramach działania o największej wartości (czyli uruchomionej ostatnio aplikacji), tworząc ukryty wirtualny wyświetlacz, szkodliwa aplikacja może pobierać dane wejściowe użytkownika nawet wtedy, gdy używa klawiatury programowej na głównym wyświetlaczu urządzenia.

Użyj com.android.internal.R.bool.config_perDisplayFocusEnabled, aby ustawić fokus na poszczególnych wyświetlaczach.

Zgodność

Problem: w Androidzie 9 i starszych w danym momencie może być aktywne maksymalnie 1 okno w systemie.

Rozwiązanie: w rzadkich przypadkach, gdy dwa okna z tego samego procesu byłyby w centrum uwagi, system skupia się tylko na oknie, które jest wyżej w kolejności Z. To ograniczenie zostało usunięte z aplikacji kierowanych na Androida 10. Od tego momentu powinny obsługiwać wiele okien jednocześnie.

Implementacja

WindowManagerService#mPerDisplayFocusEnabled określa dostępność tej funkcji. W funkcji ActivityManager zamiast globalnego śledzenia w zmiennej jest teraz używana zmienna ActivityDisplay#getFocusedStack(). ActivityDisplay#getFocusedStack()określa fokus na podstawie porządku Z zamiast przechowywania wartości w pamięci podręcznej. Dzięki temu tylko jedno źródło – WindowManager – powinno śledzić działania w kolejności Z.

ActivityStackSupervisor#getTopDisplayFocusedStack() stosuje podobne podejście w przypadkach, gdy trzeba zidentyfikować najbardziej skupiony stos w systemie. Stosy są przeszukiwane od góry do dołu w celu znalezienia pierwszego odpowiedniego stosu.

InputDispatcher może teraz mieć wiele zaznaczonych okien (po jednym na wyświetlacz). Jeśli zdarzenie wejściowe jest związane z konkretnym wyświetlaczem, jest wysyłane do okna, które jest aktualnie aktywne na tym wyświetlaczu. W przeciwnym razie jest kierowana do zaznaczonego okna, czyli wyświetlacza, z którym użytkownik ostatnio wszedł w interakcję.

Zapoznaj się z tymi dokumentami: InputDispatcher::mFocusedWindowHandlesByDisplayInputDispatcher::setFocusedDisplay(). Aplikacje objęte specjalnymi funkcjami są również aktualizowane oddzielnie w InputManagerService przez NativeInputManager::setFocusedApplication().

W systemie WindowManager zaznaczone okna są też śledzone oddzielnie. Zapoznaj się z artykułami DisplayContent#mCurrentFocusDisplayContent#mFocusedApp oraz ich odpowiednimi zastosowaniami. Powiązane metody śledzenia i aktualizowania danych o punktach skupienia zostały przeniesione z poziomu WindowManagerService do poziomu DisplayContent.