Poniżej znajdziesz informacje o zmianach wprowadzonych w tych obszarach związanych z reklamami displayowymi:
- Zmiana rozmiaru aktywności i wyświetlaczy
- Rozmiary wyświetlacza i formaty obrazu
- Zasady dotyczące reklam displayowych
- Ustawienia okna wyświetlania
- Identyfikatory wyświetlania statycznego
- Korzystanie z więcej niż 2 wyświetlaczy
- Ostrość na wyświetlacz
Zmienianie rozmiaru aktywności i wyświetlaczy
Aby wskazać, że aplikacja może nie obsługiwać trybu wielu okien ani zmiany rozmiaru, aktywności używają atrybutu resizeableActivity=false
. Do typowych problemów, które mogą wystąpić w aplikacjach po zmianie rozmiaru działań, należą:
- Aktywność może mieć inną konfigurację niż aplikacja lub inny komponent niewizualny. Częstym błędem jest odczytywanie danych o wyświetleniach z kontekstu aplikacji. Zwrócone wartości nie będą dostosowywane do danych obszaru widocznego, w którym wyświetla się aktywność.
- Aktywność może nie obsługiwać zmiany rozmiaru i ulegać awarii, wyświetlać zniekształcony interfejs lub utracić stan z powodu ponownego uruchomienia bez zapisania stanu instancji.
- Aplikacja może próbować używać bezwzględnych współrzędnych wejściowych (zamiast współrzędnych względnych względem pozycji okna), co może spowodować nieprawidłowe działanie w trybie wielu okien.
W Androidzie 7 (i nowszym) aplikację można ustawić tak, aby zawsze działała w trybie pełnoekranowym.resizeableActivity=false
W takim przypadku platforma uniemożliwia przejście działań o zmiennej wielkości w tryb podzielonego ekranu. Jeśli użytkownik spróbuje wywołać z launchera działanie, którego rozmiaru nie można zmieniać, gdy jest już w trybie podzielonego ekranu, platforma wyłączy ten tryb i uruchomi działanie w trybie pełnoekranowym.
Aplikacji, które w pliku manifestu mają ten atrybut ustawiony na false
, nie można uruchamiać w trybie wielu okien, chyba że zastosowano tryb zgodności:
- Do procesu, który zawiera wszystkie działania i komponenty inne niż działania, stosowana jest ta sama konfiguracja.
- Zastosowana konfiguracja spełnia wymagania CDD dotyczące wyświetlaczy zgodnych z aplikacjami.
W Androidzie 10 platforma nadal uniemożliwia przejście aktywnościom o zmiennej wielkości do trybu podzielonego ekranu, ale można je tymczasowo skalować, jeśli aktywność zadeklarowała stałą orientację lub współczynnik proporcji. W przeciwnym razie aktywność zmieni rozmiar, aby wypełnić cały ekran, tak jak w Androidzie 9 i starszych wersjach.
Domyślna implementacja stosuje te zasady:
Gdy aktywność zadeklarowana jako niezgodna z wieloma oknami za pomocą atrybutu android:resizeableActivity
spełnia jeden z warunków opisanych poniżej, a zastosowana konfiguracja ekranu musi się zmienić, aktywność i proces są zapisywane z pierwotną konfiguracją, a użytkownik ma możliwość ponownego uruchomienia procesu aplikacji, aby używać zaktualizowanej konfiguracji ekranu.
- Czy orientacja jest stała dzięki zastosowaniu
android:screenOrientation
- Aplikacja ma domyślny maksymalny lub minimalny format obrazu, ponieważ jest kierowana na określony poziom API lub deklaruje format obrazu w sposób jawny
Ilustracja przedstawiająca aktywność o zadeklarowanym współczynniku proporcji, której rozmiaru nie można zmieniać. Po złożeniu urządzenia okno jest skalowane w dół, aby dopasować się do obszaru, przy zachowaniu współczynnika proporcji za pomocą odpowiedniego letterboxingu. Dodatkowo za każdym razem, gdy zmienia się obszar wyświetlania aktywności, użytkownik ma do dyspozycji opcję ponownego uruchomienia aktywności.
Po rozłożeniu urządzenia konfiguracja, rozmiar i proporcje 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
Nie można zmienić rozmiaru aktywności o stałej orientacji lub stałym współczynniku proporcji. W kodzie nazywa się to trybem zgodności rozmiaru (SCM). Warunek jest zdefiniowany w ActivityRecord#shouldUseSizeCompatMode()
. Gdy uruchamiana jest aktywność SCM, konfiguracja związana z ekranem (np. rozmiar lub gęstość) jest ustalana w żądanej konfiguracji zastępowania, więc aktywność nie jest już zależna 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 aktywność SCM używa innej konfiguracji ekranu niż kontener (np. rozmiar wyświetlacza jest zmieniony lub aktywność została przeniesiona na inny wyświetlacz), wartość ActivityRecord#inSizeCompatMode()
jest prawdziwa, a SizeCompatModeActivityController
(w interfejsie systemu) otrzymuje wywołanie zwrotne, aby wyświetlić przycisk ponownego uruchomienia procesu.
Rozmiary wyświetlacza i formaty obrazu
Android 10 obsługuje nowe formaty obrazu, od wysokich formatów długich i wąskich ekranów po formaty 1:1. Aplikacje mogą określać ApplicationInfo#maxAspectRatio
i ApplicationInfo#minAspectRatio
ekranu, które są w stanie obsługiwać.
Rysunek 1. Przykładowe współczynniki proporcji aplikacji obsługiwane na Androidzie 10
Urządzenia mogą mieć ekrany dodatkowe o rozmiarach i rozdzielczościach mniejszych niż wymagane w Androidzie 9 i starszych wersjach (minimum 2,5 cala szerokości lub wysokości, minimum 320 DP w przypadku smallestScreenWidth
), ale tylko aktywności, które obsługują te małe ekrany, mogą być na nich umieszczane.
Aplikacje mogą wyrazić zgodę, deklarując minimalny obsługiwany rozmiar, który jest mniejszy lub równy rozmiarowi docelowego wyświetlacza. W tym celu użyj atrybutów układu aktywności android:minHeight
i android:minWidth
w pliku AndroidManifest.
Zasady wyświetlania
Android 10 rozdziela i przenosi niektóre zasady wyświetlania z domyślnej implementacji WindowManagerPolicy
PhoneWindowManager
do klas poszczególnych wyświetlaczy, takich jak:
- Stan wyświetlacza i rotacja
- Śledzenie niektórych klawiszy i zdarzeń związanych z ruchem
- Interfejs systemu i okna dekoracyjne
W Androidzie 9 (i starszych wersjach) klasa PhoneWindowManager
obsługiwała zasady wyświetlania, stan i ustawienia, obracanie, śledzenie ramki okna dekoracji i inne funkcje. W Androidzie 10 większość tych funkcji została przeniesiona do klasy DisplayPolicy
, z wyjątkiem śledzenia rotacji, które zostało przeniesione do klasy DisplayRotation
.
Ustawienia okna wyświetlania
W Androidzie 10 rozszerzyliśmy konfigurowalne ustawienie okien na poszczególnych wyświetlaczach, aby obejmowało:
- Domyślny tryb wyświetlania
- Wartości overscanu
- Obrót użytkownika i tryb obrotu
- Wymuszony rozmiar, gęstość i tryb skalowania
- Tryb usuwania treści (gdy wyświetlacz jest usunięty)
- Obsługa dekoracji systemowych i edytora IME
Klasa DisplayWindowSettings
zawiera ustawienia tych opcji. Są one zapisywane na dysku w /data
partycjidisplay_settings.xml
za każdym razem, gdy zmieniane jest ustawienie. Więcej informacji znajdziesz w sekcjach DisplayWindowSettings.AtomicFileStorage
i DisplayWindowSettings#writeSettings()
. Producenci urządzeń mogą podawać wartości domyślne w display_settings.xml
w konfiguracji urządzenia. Ponieważ jednak plik jest przechowywany w /data
, w przypadku wymazania danych może być potrzebna dodatkowa logika, aby przywrócić plik.
Domyślnie Android 10 używa identyfikatora DisplayInfo#uniqueId
do identyfikowania wyświetlacza podczas zapisywania ustawień. W przypadku wszystkich wyświetleń należy podać wartość parametru uniqueId
. Dodatkowo jest stabilny w przypadku wyświetlaczy fizycznych i sieciowych. Możesz też użyć portu wyświetlacza fizycznego jako identyfikatora, 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 wyświetlanego w pamięci. Więcej informacji znajdziesz w sekcji Statyczne identyfikatory wyświetlania.
Ustawienia są przechowywane w katalogu /data
z przyczyn historycznych. Pierwotnie służyły one do przechowywania ustawień użytkownika, takich jak obracanie ekranu.
Identyfikatory reklam statycznych
Android 9 (i starsze wersje) nie udostępniał stabilnych identyfikatorów wyświetlaczy w ramach platformy. Gdy wyświetlacz został dodany do systemu, dla tego wyświetlacza wygenerowano identyfikator Display#mDisplayId
lub DisplayInfo#displayId
, zwiększając statyczny licznik. Jeśli system dodał i usunął ten sam wyświetlacz, powstał inny identyfikator.
Jeśli urządzenie miało wiele wyświetlaczy dostępnych od momentu uruchomienia, wyświetlacze mogły mieć przypisane różne identyfikatory w zależności od czasu. Android 9 (i starsze wersje) zawierał DisplayInfo#uniqueId
, ale nie zawierał wystarczającej ilości informacji, aby odróżnić wyświetlacze, ponieważ wyświetlacze fizyczne były identyfikowane jako local:0
lub local:1
, co oznaczało wyświetlacz wbudowany i zewnętrzny.
Android 10 zmienia DisplayInfo#uniqueId
, aby dodać stabilny identyfikator i odróżnić wyświetlacze lokalne, sieciowe i wirtualne.
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
, identyfikator wyświetlania, który jest stabilny po ponownym uruchomieniu. W Androidzie 10 DisplayAddress
obsługuje wyświetlacze fizyczne i sieciowe. DisplayAddress.Physical
zawiera stały identyfikator wyświetlania (taki sam jak w uniqueId
) i można go utworzyć za pomocą DisplayAddress#fromPhysicalDisplayId()
.
Android 10 udostępnia też wygodną metodę uzyskiwania informacji o porcie (Physical#getPort()
). Można jej używać w ramach statycznego identyfikowania wyświetlaczy. Jest on używany na przykład 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 za pomocą statycznych identyfikatorów wyświetlaczy, takich jak porty wyświetlaczy fizycznych. Te metody są ukryte i 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 (zależny od platformy) 8-bitowy numer portu, który identyfikuje fizyczne złącze wyjścia wyświetlacza, a także blok EDID wyświetlacza.
SurfaceFlinger wyodrębnia informacje o producencie lub modelu z EDID, aby generować stabilne 64-bitowe identyfikatory wyświetlacza udostępniane platformie. Jeśli ta metoda nie jest obsługiwana lub zwraca błąd, SurfaceFlinger wraca 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 to 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"
Używanie więcej niż 2 wyświetlaczy
W Androidzie 9 (i starszych wersjach) SurfaceFlinger i DisplayManagerService
zakładały istnienie co najwyżej 2 wyświetlaczy fizycznych z zakodowanymi na stałe identyfikatorami 0 i 1.
Od Androida 10 SurfaceFlinger może korzystać z interfejsu Hardware Composer (HWC) API do generowania stabilnych identyfikatorów wyświetlaczy, co umożliwia zarządzanie dowolną liczbą wyświetlaczy fizycznych. Więcej informacji znajdziesz w artykule Statyczne identyfikatory wyświetlania.
Platforma może wyszukać token IBinder
dla wyświetlacza fizycznego za pomocą SurfaceControl#getPhysicalDisplayToken
po uzyskaniu 64-bitowego identyfikatora wyświetlacza z SurfaceControl#getPhysicalDisplayIds
lub z DisplayEventReceiver
zdarzenia hotplug.
W Androidzie 10 (i starszych wersjach) główny wyświetlacz wewnętrzny jest oznaczony jako TYPE_INTERNAL
, a wszystkie wyświetlacze dodatkowe 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 tego problemu kod specyficzny dla urządzenia może przyjmować założenia dotyczące tego, DisplayAddress.Physical#getPort
czy HWC jest znany, a logika przydzielania portów jest przewidywalna.
To ograniczenie zostało usunięte w Androidzie 11 (i nowszych wersjach).
- W Androidzie 11 pierwszym wyświetlaczem zgłoszonym podczas uruchamiania jest wyświetlacz główny. Typ połączenia (wewnętrzne lub zewnętrzne) nie ma znaczenia. Nadal jednak nie można odłączyć wyświetlacza głównego, co oznacza, że w praktyce musi to być wyświetlacz wewnętrzny. Pamiętaj, że niektóre telefony składane mają kilka ekranów wewnętrznych.
- Wyświetlacze dodatkowe są prawidłowo klasyfikowane jako
Display.TYPE_INTERNAL
lubDisplay.TYPE_EXTERNAL
(wcześniejDisplay.TYPE_BUILT_IN
iDisplay.TYPE_HDMI
) w zależności od typu połączenia.
Implementacja
Na 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.
Od Androida 10 wyświetlacze mają stabilne i trwałe identyfikatory, co umożliwia usłudze SurfaceFlinger i DisplayManagerService
śledzenie więcej niż 2 wyświetlaczy i rozpoznawanie wyświetlaczy, które były już wcześniej używane. 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świetlacza dla wyświetlaczy fizycznych i wirtualnych HWC. Identyfikatory są wyrażane za pomocą typu opcji, w którym wartość null reprezentuje nieprawidłowy wyświetlacz lub wirtualny wyświetlacz inny niż HWC. Bez obsługi HWC SurfaceFlinger wraca do starszego działania z maksymalnie 2 wyświetlaczami fizycznymi.
Skupienie na wyświetlaczu
Aby obsługiwać kilka źródeł wejściowych, które jednocześnie są kierowane na poszczególne wyświetlacze, Android 10 może być skonfigurowany tak, aby obsługiwać wiele okien z fokusem, maksymalnie po jednym na wyświetlacz. Jest to przeznaczone tylko dla specjalnych typów urządzeń, gdy wielu użytkowników korzysta z tego samego urządzenia w tym samym czasie i używa różnych metod lub urządzeń wejściowych, np. Androida Automotive.
Zdecydowanie zalecamy, aby ta funkcja nie była włączona na zwykłych urządzeniach, w tym na urządzeniach z wieloma ekranami lub urządzeniach używanych do obsługi aplikacji w trybie podobnym do komputerowego. Wynika to głównie z obaw o bezpieczeństwo, które mogą powodować, że użytkownicy nie będą wiedzieć, które okno jest aktywne.
Wyobraź sobie użytkownika, który wpisuje bezpieczne informacje w polu tekstowym, 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łania, w tym wprowadzać tekst. Zarówno legalne, jak i złośliwe działania są aktywne i w obu przypadkach wyświetla się wskaźnik aktywnego wprowadzania (migający kursor).
Jednak dane wejściowe z klawiatury (sprzętowej lub programowej) są wprowadzane tylko w najwyżej położonej aktywności (aplikacji, która została ostatnio uruchomiona). Tworząc ukryty wirtualny wyświetlacz, złośliwa aplikacja może przechwytywać dane wejściowe użytkownika, nawet gdy używa on klawiatury programowej na głównym wyświetlaczu urządzenia.
Użyj com.android.internal.R.bool.config_perDisplayFocusEnabled
, aby ustawić ostrość na poszczególnych wyświetlaczach.
Zgodność
Problem: w starszych wersjach Androida (do wersji 9 włącznie) w danym momencie aktywny może być tylko jeden element w systemie.
Rozwiązanie: w rzadkich przypadkach, gdy dwa okna z tego samego procesu są aktywne, system aktywuje tylko okno, które znajduje się wyżej w kolejności Z. To ograniczenie nie dotyczy aplikacji na Androida 10, które powinny obsługiwać jednoczesne skupianie się na wielu oknach.
Implementacja
WindowManagerService#mPerDisplayFocusEnabled
określa dostępność tej funkcji. W ActivityManager
zmienna ActivityDisplay#getFocusedStack()
jest teraz używana zamiast śledzenia globalnego. ActivityDisplay#getFocusedStack()
określa fokus na podstawie kolejności Z zamiast buforowania wartości. Dzięki temu tylko jedno źródło, WindowManager, musi śledzić kolejność aktywności.
ActivityStackSupervisor#getTopDisplayFocusedStack()
stosuje podobne podejście w przypadku sytuacji, w których trzeba zidentyfikować najwyżej położony stos w systemie. Stosy są sprawdzane od góry do dołu w poszukiwaniu pierwszego odpowiedniego stosu.
InputDispatcher
może teraz mieć wiele okien w trybie skupienia (po jednym na wyświetlacz). Jeśli zdarzenie wejściowe jest specyficzne dla wyświetlacza, jest wysyłane do okna, na którym jest fokus, na odpowiednim wyświetlaczu. W przeciwnym razie jest on wysyłany do aktywnego okna na aktywnym wyświetlaczu, czyli na wyświetlaczu, z którym użytkownik ostatnio wchodził w interakcję.
Zobacz InputDispatcher::mFocusedWindowHandlesByDisplay
i InputDispatcher::setFocusedDisplay()
. Aplikacje, na których skupia się uwaga użytkownika, są też aktualizowane
osobno w InputManagerService za pomocą
NativeInputManager::setFocusedApplication()
.
W WindowManager
aktywne okna są też śledzone osobno.
Zobacz DisplayContent#mCurrentFocus
i DisplayContent#mFocusedApp
oraz ich zastosowania. Powiązane metody śledzenia i aktualizowania ostrości zostały przeniesione z WindowManagerService
do DisplayContent
.