Obsługa wyświetlacza

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Aktualizacje wprowadzone w tych obszarach specyficznych dla wyświetlacza są przedstawione poniżej:

Zmiana rozmiaru działań i wyświetlaczy

Aby wskazać, że aplikacja może nie obsługiwać trybu wielu okien lub zmiany rozmiaru, działania używają resizeableActivity=false . Typowe problemy napotykane przez aplikacje podczas zmiany rozmiaru aktywności obejmują:

  • Działanie może mieć inną konfigurację niż aplikacja lub inny składnik niewizualny. Częstym błędem jest odczytywanie metryk wyświetlania z kontekstu aplikacji. Zwracane wartości nie będą dostosowywane do metryk widocznego obszaru, w których wyświetlane jest działanie.
  • Aktywność może nie obsługiwać zmiany rozmiaru i awarii, wyświetlać zniekształcony interfejs użytkownika lub utracić stan z powodu ponownego uruchomienia bez zapisywania stanu instancji.
  • Aplikacja może próbować użyć bezwzględnych współrzędnych wejściowych (zamiast tych odnoszących się do pozycji okna), co może spowodować uszkodzenie danych wejściowych w wielu oknach.

W systemie Android 7 (i nowszych) aplikacja może być ustawiona resizeableActivity=false , aby zawsze działała w trybie pełnoekranowym. W takim przypadku platforma zapobiega przechodzeniu do podzielonego ekranu działań, których nie można zmienić. Jeśli użytkownik spróbuje wywołać działanie bez zmiany rozmiaru z programu uruchamiającego, gdy jest już w trybie podzielonego ekranu, platforma wyjdzie z trybu podzielonego ekranu i uruchomi działanie bez zmiany rozmiaru w trybie pełnoekranowym.

Aplikacji, które jawnie ustawią ten atrybut na wartość false w manifeście, nie można uruchamiać w trybie wielu okien, chyba że zastosowano tryb zgodności:

  • Ta sama konfiguracja jest stosowana do procesu, który zawiera wszystkie działania i komponenty nieaktywne.
  • Zastosowana konfiguracja spełnia wymagania CDD dla wyświetlaczy zgodnych z aplikacjami.

W systemie Android 10 platforma nadal uniemożliwia przechodzenie działań bez zmiany rozmiaru do trybu podzielonego ekranu, ale można je tymczasowo skalować, jeśli działanie zadeklarowało stałą orientację lub współczynnik proporcji. Jeśli nie, aktywność zmienia się, aby wypełnić cały ekran, tak jak w Androidzie 9 i niższych.

Domyślna implementacja dotyczy następujących zasad:

Gdy aktywność zadeklarowana jako niekompatybilna z trybem wielookienkowym poprzez użycie atrybutu android:resizeableActivity i gdy ta aktywność spełnia jeden z warunków opisanych poniżej, to gdy zastosowana konfiguracja ekranu musi się zmienić, aktywność i proces są zapisywane z oryginalną konfiguracją a użytkownik otrzymuje afordancję na ponowne uruchomienie procesu aplikacji, aby użyć zaktualizowanej konfiguracji ekranu.

  • Czy orientacja jest ustalona za pomocą aplikacji android:screenOrientation
  • Aplikacja ma domyślny maksymalny lub minimalny współczynnik proporcji przez kierowanie na poziom interfejsu API lub jawnie deklaruje współczynnik proporcji

Ten rysunek przedstawia aktywność, której rozmiar nie może być zmieniany, z zadeklarowanym współczynnikiem proporcji. Podczas składania urządzenia okno jest zmniejszane, aby dopasować się do obszaru, zachowując proporcje przy użyciu odpowiedniego letterboxa. Ponadto użytkownik otrzymuje opcję ponownego uruchomienia działania za każdym razem, gdy zmienia się obszar wyświetlania działania.

Podczas rozkładania urządzenia konfiguracja, rozmiar i proporcje działania nie zmieniają się, ale wyświetlana jest opcja ponownego uruchomienia działania.

Gdy resizeableActivity nie jest ustawiona (lub jest ustawiona na true ), aplikacja w pełni obsługuje zmianę rozmiaru.

Realizacja

Działanie bez zmiany rozmiaru ze stałą orientacją lub współczynnikiem proporcji jest nazywane trybem zgodności rozmiaru (SCM) w kodzie. Warunek jest zdefiniowany w ActivityRecord#shouldUseSizeCompatMode() . Po uruchomieniu działania SCM konfiguracja związana z ekranem (taka jak rozmiar lub gęstość) jest ustalana w żądanej konfiguracji nadpisania, dzięki czemu działanie nie jest już zależne od bieżącej konfiguracji wyświetlania.

Jeśli aktywność SCM nie może wypełnić całego ekranu, jest wyrównana do góry i wyśrodkowana w poziomie. Granice aktywności są obliczane przez AppWindowToken#calculateCompatBoundsTransformation() .

Gdy działanie SCM używa innej konfiguracji ekranu niż jego kontener (na przykład, rozmiar ekranu jest zmieniany lub działanie przeniesione na inny ekran), ActivityRecord#inSizeCompatMode() ma wartość true i SizeCompatModeActivityController (w interfejsie użytkownika systemu) odbiera wywołanie zwrotne, aby pokazać proces przycisk restartu.

Rozmiary wyświetlacza i proporcje

Android 10 zapewnia obsługę nowych proporcji, od wysokich proporcji długich i cienkich ekranów do proporcji 1:1. Aplikacje mogą definiować ApplicationInfo#maxAspectRatio i ApplicationInfo#minAspectRatio ekranu, który są w stanie obsłużyć.

proporcje aplikacji w Androidzie 10

Rysunek 1. Przykładowe współczynniki aplikacji obsługiwane w systemie Android 10

Implementacje urządzeń mogą mieć dodatkowe wyświetlacze o rozmiarach i rozdzielczościach mniejszych niż te wymagane przez system Android 9 i niższych (minimum 2,5 cala szerokości lub wysokości, minimum 320 DP dla smallestScreenWidth ekranów), ale tylko czynności, które zdecydują się obsługiwać te małe wyświetlacze, mogą być umieszczone tam.

Aplikacje mogą się włączyć, deklarując minimalny obsługiwany rozmiar, który jest mniejszy niż jeden równy docelowemu rozmiarowi ekranu. W tym celu użyj atrybutów układu działań android:minHeight i android:minWidth w AndroidManifest.

Zasady wyświetlania

System Android 10 oddziela i przenosi określone zasady wyświetlania z domyślnej implementacji WindowManagerPolicy w PhoneWindowManager do klas dla poszczególnych ekranów, takich jak:

  • Stan wyświetlacza i obrót
  • Niektóre klawisze i śledzenie zdarzeń ruchu
  • Systemowy interfejs użytkownika i okna dekoracji

W systemie Android 9 (i niższym) klasa PhoneWindowManager obsługiwała zasady wyświetlania, stan i ustawienia, obracanie, śledzenie ramek okien dekoracji i inne. Android 10 przenosi większość tego do klasy DisplayPolicy , z wyjątkiem śledzenia rotacji, które zostało przeniesione do DisplayRotation .

Ustawienia okna wyświetlania

W systemie Android 10 konfigurowalne ustawienie okien dla poszczególnych ekranów zostało rozszerzone o:

  • Domyślny tryb wyświetlania okienek
  • Wartości overscan
  • Rotacja użytkownika i tryb rotacji
  • Wymuszony rozmiar, gęstość i tryb skalowania
  • Tryb usuwania treści (gdy wyświetlacz jest usunięty)
  • Wsparcie dla dekoracji systemu i IME

Klasa DisplayWindowSettings zawiera ustawienia tych opcji. Są one utrwalane na dysku w partycji /data w pliku display_settings.xml po każdej zmianie ustawienia. Aby uzyskać szczegółowe informacje, zobacz DisplayWindowSettings.AtomicFileStorage i DisplayWindowSettings#writeSettings() . Producenci urządzeń mogą podać wartości domyślne w display_settings.xml dla konfiguracji swoich urządzeń. Jednak ponieważ plik jest przechowywany w /data , może być potrzebna dodatkowa logika, aby przywrócić plik, jeśli zostanie wymazany.

Domyślnie system Android 10 używa DisplayInfo#uniqueId jako identyfikatora wyświetlacza podczas utrwalania ustawień. uniqueId powinien być wypełniony dla wszystkich wyświetlaczy. Ponadto jest stabilny dla wyświetlaczy fizycznych i sieciowych. Możliwe jest również użycie portu fizycznego wyświetlacza jako identyfikatora, który można ustawić w DisplayWindowSettings#mIdentifier . Przy każdym zapisie zapisywane są wszystkie ustawienia, dzięki czemu można bezpiecznie zaktualizować klucz używany do wyświetlania wpisu w pamięci. Aby uzyskać szczegółowe informacje, zobacz Statyczne identyfikatory wyświetlania .

Ustawienia są zachowywane w katalogu /data ze względów historycznych. Pierwotnie były używane do utrwalania ustawień użytkownika, takich jak obracanie ekranu.

Statyczne identyfikatory wyświetlania

Android 9 (i starsze) nie zapewniał stabilnych identyfikatorów wyświetlaczy w ramach frameworka. Gdy ekran został dodany do systemu, dla tego ekranu został wygenerowany Display#mDisplayId lub DisplayInfo#displayId przez zwiększenie licznika statycznego. Jeśli system dodał i usunął ten sam wyświetlacz, powstał inny identyfikator.

Jeśli urządzenie miało wiele wyświetlaczy dostępnych z rozruchu, wyświetlaczom można było przypisać różne identyfikatory, w zależności od czasu. Chociaż system Android 9 (i wcześniejszy) zawierał DisplayInfo#uniqueId , nie zawierał wystarczających informacji do rozróżnienia między wyświetlaczami, ponieważ wyświetlacze fizyczne zostały zidentyfikowane jako local:0 lub local:1 , aby reprezentować wbudowany i zewnętrzny wyświetlacz.

Android 10 zmienia DisplayInfo#uniqueId , aby dodać stabilny identyfikator i rozróżniać wyświetlacze lokalne, sieciowe i wirtualne.

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

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

Android 10 zapewnia również wygodną metodę uzyskiwania informacji o porcie ( Physical#getPort() ). Ta metoda może być używana w ramach do statycznej identyfikacji wyświetlaczy. Na przykład jest używany w DisplayWindowSettings ). DisplayAddress.Network zawiera adres MAC i można go utworzyć za pomocą DisplayAddress#fromMacAddress() .

Te dodatki umożliwiają producentom urządzeń identyfikowanie wyświetlaczy w statycznych konfiguracjach z wieloma wyświetlaczami oraz konfigurowanie różnych ustawień i funkcji systemu przy użyciu statycznych identyfikatorów wyświetlaczy, takich jak porty dla wyświetlaczy fizycznych. Te metody są ukryte i są przeznaczone tylko do użytku w ramach system_server .

Biorąc pod uwagę identyfikator wyświetlacza HWC (który może być nieprzezroczysty i nie zawsze stabilny), ta metoda zwraca (specyficzny dla platformy) 8-bitowy numer portu, który identyfikuje fizyczne złącze wyjściowe wyświetlacza, a także obiekt blob EDID wyświetlacza. SurfaceFlinger wyodrębnia informacje o producencie lub modelu z identyfikatora EDID w celu wygenerowania stabilnych 64-bitowych identyfikatorów wyświetlania wystawionych na działanie struktury. Jeśli ta metoda nie jest obsługiwana lub występują błędy, SurfaceFlinger powraca do starszego trybu MD, w którym DisplayInfo#address ma wartość null, a DisplayInfo#uniqueId jest zakodowany na stałe, jak opisano powyżej.

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

$ 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ż dwóch wyświetlaczy

W systemie Android 9 (i niższym) SurfaceFlinger i DisplayManagerService zakładały istnienie co najwyżej dwóch fizycznych wyświetlaczy z zakodowanymi na stałe identyfikatorami 0 i 1.

Począwszy od Androida 10, SurfaceFlinger może wykorzystywać interfejs API Hardware Composer (HWC) do generowania stabilnych identyfikatorów wyświetlania, co umożliwia zarządzanie dowolną liczbą fizycznych wyświetlaczy. Aby dowiedzieć się więcej, zobacz Statyczne identyfikatory wyświetlania .

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

W systemie Android 10 (i niższych) główny wyświetlacz wewnętrzny to TYPE_INTERNAL , a wszystkie wyświetlacze pomocnicze są oznaczone jako TYPE_EXTERNAL niezależnie od typu połączenia. Dlatego dodatkowe wyświetlacze wewnętrzne są traktowane jako zewnętrzne. Jako obejście kod specyficzny dla urządzenia może przyjmować założenia dotyczące DisplayAddress.Physical#getPort , jeśli HWC jest znany, a logika alokacji portów jest przewidywalna.

To ograniczenie zostało usunięte w systemie Android 11 (i nowszym).

  • W systemie Android 11 pierwszy wyświetlacz zgłoszony podczas rozruchu jest wyświetlaczem podstawowym. Typ połączenia (wewnętrzne lub zewnętrzne) nie ma znaczenia. Prawdą jest jednak, że wyświetlacza głównego nie można odłączyć, co oznacza, że ​​w praktyce musi to być wyświetlacz wewnętrzny. Zwróć uwagę, że niektóre składane telefony mają wiele wewnętrznych wyświetlaczy.
  • Wyświetlacze pomocnicze są prawidłowo klasyfikowane jako Display.TYPE_INTERNAL lub Display.TYPE_EXTERNAL (wcześniej znane odpowiednio jako Display.TYPE_BUILT_IN i Display.TYPE_HDMI ) w zależności od typu połączenia.

Realizacja

W systemie Android 9 i niższych wyświetlacze są identyfikowane przez 32-bitowe identyfikatory, gdzie 0 to wyświetlacz wewnętrzny, 1 to wyświetlacz zewnętrzny, [2, INT32_MAX] to wyświetlacze wirtualne HWC, a -1 oznacza nieprawidłowy wyświetlacz lub nie-HWC wirtualny wyświetlacz.

Począwszy od Androida 10, wyświetlacze otrzymują stabilne i trwałe identyfikatory, co pozwala SurfaceFlinger i DisplayManagerService na śledzenie więcej niż dwóch wyświetlaczy i rozpoznawanie poprzednio widzianych wyświetlaczy. Jeśli HWC obsługuje IComposerClient.getDisplayIdentificationData i dostarcza dane identyfikacyjne ekranu, SurfaceFlinger analizuje strukturę EDID i przydziela stabilne 64-bitowe identyfikatory wyświetlania dla wyświetlaczy fizycznych i wirtualnych HWC. Identyfikatory są wyrażane przy użyciu typu opcji, gdzie wartość null reprezentuje nieprawidłowy ekran lub ekran wirtualny inny niż HWC. Bez obsługi HWC SurfaceFlinger powraca do starszego zachowania z co najwyżej dwoma fizycznymi wyświetlaczami.

Nacisk na wyświetlacz

Aby obsługiwać kilka źródeł danych wejściowych, które są kierowane na poszczególne wyświetlacze w tym samym czasie, system Android 10 można skonfigurować tak, aby obsługiwał wiele skoncentrowanych okien, maksymalnie jedno na wyświetlacz. Jest to przeznaczone tylko dla specjalnych typów urządzeń, gdy wielu użytkowników wchodzi w interakcję z tym samym urządzeniem w tym samym czasie i używa różnych metod wprowadzania lub urządzeń, takich jak Android Automotive.

Zdecydowanie zaleca się, aby ta funkcja nie była włączana na zwykłych urządzeniach, w tym na urządzeniach z wieloma ekranami lub urządzeniach używanych do obsługi komputerów stacjonarnych. Wynika to głównie z obawy o bezpieczeństwo, która może spowodować, że użytkownicy będą się zastanawiać, które okno ma fokus.

Wyobraź sobie użytkownika, który wprowadza bezpieczne informacje w polu wprowadzania tekstu, na przykład logując się do aplikacji bankowej lub wprowadzając tekst zawierający poufne informacje. Złośliwa aplikacja może stworzyć wirtualny wyświetlacz pozaekranowy, za pomocą którego można wykonać czynność, również z polem wprowadzania tekstu. Uzasadnione i złośliwe działania są skoncentrowane i obie wyświetlają wskaźnik aktywnego wejścia (migający kursor).

Ponieważ jednak dane wprowadzane z klawiatury (sprzęt lub oprogramowanie) są wprowadzane tylko do najwyższej aktywności (aplikacji, która została ostatnio uruchomiona), przez utworzenie ukrytego wirtualnego wyświetlacza złośliwa aplikacja może przechwycić dane użytkownika, nawet przy użyciu klawiatury programowej na głównym wyświetlaczu urządzenia.

Użyj com.android.internal.R.bool.config_perDisplayFocusEnabled , aby ustawić fokus na ekran.

Zgodność

Problem: W systemie Android 9 i niższych w danym momencie aktywne jest co najwyżej jedno okno w systemie.

Rozwiązanie: W rzadkim przypadku, gdy dwa okna z tego samego procesu byłyby skoncentrowane, system zapewnia koncentrację tylko na oknie, które jest wyżej w kolejności Z. To ograniczenie jest usuwane w przypadku aplikacji przeznaczonych dla systemu Android 10, w którym to momencie oczekuje się, że mogą obsługiwać jednocześnie wiele okien.

Realizacja

WindowManagerService#mPerDisplayFocusEnabled kontroluje dostępność tej funkcji. W ActivityManager , ActivityDisplay#getFocusedStack() jest teraz używana zamiast globalnego śledzenia w zmiennej. ActivityDisplay#getFocusedStack() określa fokus na podstawie kolejności Z zamiast buforowania wartości. Dzieje się tak, aby tylko jedno źródło, WindowManager, mogło śledzić kolejność działań Z.

ActivityStackSupervisor#getTopDisplayFocusedStack() stosuje podobne podejście w przypadkach, gdy należy zidentyfikować najwyższy stos w systemie. Stosy są przesuwane od góry do dołu w poszukiwaniu pierwszego odpowiedniego stosu.

InputDispatcher może teraz mieć wiele skoncentrowanych okien (po jednym na ekran). Jeśli zdarzenie wejściowe jest specyficzne dla ekranu, jest wywoływane do aktywnego okna na odpowiednim ekranie. W przeciwnym razie jest wysyłany do aktywnego okna na aktywnym ekranie, czyli do ekranu, z którym użytkownik korzystał ostatnio.

Zobacz InputDispatcher::mFocusedWindowHandlesByDisplay i InputDispatcher::setFocusedDisplay() . Skupione aplikacje są również aktualizowane oddzielnie w InputManagerService za pośrednictwem NativeInputManager::setFocusedApplication() .

W WindowManager skoncentrowane okna są również śledzone oddzielnie. Zobacz DisplayContent#mCurrentFocus i DisplayContent#mFocusedApp oraz odpowiednie zastosowania. Powiązane metody śledzenia i aktualizowania fokusu zostały przeniesione z WindowManagerService do DisplayContent .