بهروزرسانیهای انجامشده در این بخشهای خاص نمایشگر در زیر ارائه شده است:
- تغییر اندازه فعالیتها و نمایشگرها
- اندازهها و نسبتهای ابعاد نمایشگر
- سیاستهای نمایش
- نمایش تنظیمات پنجره
- شناسههای نمایش استاتیک
- استفاده از بیش از دو نمایشگر
- تمرکز بر هر نمایشگر
تغییر اندازه فعالیتها و نمایشگرها
برای نشان دادن اینکه یک برنامه ممکن است از حالت چند پنجرهای یا تغییر اندازه پشتیبانی نکند، فعالیتها از ویژگی resizeableActivity=false استفاده میکنند. مشکلات رایجی که برنامهها هنگام تغییر اندازه فعالیتها با آن مواجه میشوند عبارتند از:
- یک فعالیت میتواند پیکربندی متفاوتی از برنامه یا یک مؤلفه غیر بصری دیگر داشته باشد. یک اشتباه رایج، خواندن معیارهای نمایش از متن برنامه است. مقادیر برگردانده شده با معیارهای ناحیه قابل مشاهده که یک فعالیت در آن نمایش داده میشود، تنظیم نمیشوند.
- یک فعالیت ممکن است تغییر اندازه را تحمل نکند و از کار بیفتد، یک رابط کاربری تحریف شده نمایش دهد یا به دلیل راهاندازی مجدد بدون ذخیره وضعیت نمونه، وضعیت را از دست بدهد.
- یک برنامه ممکن است سعی کند از مختصات ورودی مطلق (به جای مختصات نسبت به موقعیت پنجره) استفاده کند، که ممکن است ورودی را در حالت چند پنجرهای خراب کند.
در اندروید ۷ (و بالاتر)، میتوان یک برنامه را با تنظیم resizeableActivity=false طوری تنظیم کرد که همیشه در حالت تمام صفحه اجرا شود. در این حالت، پلتفرم از رفتن فعالیتهای غیرقابل تغییر اندازه به حالت تقسیم صفحه جلوگیری میکند. اگر کاربر سعی کند یک فعالیت غیرقابل تغییر اندازه را از لانچر در حالی که در حالت تقسیم صفحه است، فراخوانی کند، پلتفرم از حالت تقسیم صفحه خارج شده و فعالیت غیرقابل تغییر اندازه را در حالت تمام صفحه اجرا میکند.
برنامههایی که صریحاً این ویژگی را در مانیفست روی false تنظیم میکنند، نباید در حالت چند پنجرهای اجرا شوند، مگر اینکه حالت سازگاری اعمال شده باشد:
- همین پیکربندی برای فرآیند اعمال میشود که شامل تمام اجزای فعالیتها و غیر فعالیتها است.
- پیکربندی اعمالشده، الزامات CDD برای نمایشگرهای سازگار با برنامه را برآورده میکند.
در اندروید ۱۰، پلتفرم همچنان از ورود فعالیتهای غیرقابل تغییر اندازه به حالت تقسیم صفحه جلوگیری میکند، اما اگر فعالیت جهت یا نسبت ابعاد ثابتی را اعلام کرده باشد، میتوان آنها را به طور موقت مقیاسبندی کرد. در غیر این صورت، فعالیت تغییر اندازه میدهد تا کل صفحه را مانند اندروید ۹ و پایینتر پر کند.
پیادهسازی پیشفرض، سیاست زیر را اعمال میکند:
وقتی یک فعالیت با استفاده از ویژگی android:resizeableActivity با قابلیت چند پنجرهای ناسازگار اعلام شود و آن فعالیت یکی از شرایط شرح داده شده در زیر را داشته باشد، وقتی پیکربندی صفحه نمایش اعمال شده باید تغییر کند، فعالیت و فرآیند با پیکربندی اصلی ذخیره میشوند و به کاربر این امکان داده میشود که فرآیند برنامه را مجدداً راهاندازی کند تا از پیکربندی صفحه نمایش بهروزرسانی شده استفاده کند.
- آیا جهتگیری از طریق کاربرد
android:screenOrientationثابت شده است؟ - برنامه با هدف قرار دادن سطح API یا اعلام صریح نسبت ابعاد، حداکثر یا حداقل نسبت ابعاد پیشفرض را دارد.
این شکل یک فعالیت غیرقابل تغییر اندازه با نسبت ابعاد اعلامشده را نشان میدهد. هنگام تا کردن دستگاه، پنجره برای تناسب با مساحت مورد نظر کوچک میشود و در عین حال نسبت ابعاد با استفاده از جعبه حروف مناسب حفظ میشود. علاوه بر این، هر بار که مساحت نمایش فعالیت تغییر میکند، گزینه راهاندازی مجدد فعالیت در اختیار کاربر قرار میگیرد.
هنگام باز کردن دستگاه، پیکربندی، اندازه و نسبت ابعاد فعالیت تغییر نمیکند، اما گزینهای برای راهاندازی مجدد فعالیت نمایش داده میشود.
وقتی resizeableActivity تنظیم نشده باشد (یا روی true تنظیم شده باشد)، برنامه کاملاً از تغییر اندازه پشتیبانی میکند.
پیادهسازی
یک فعالیت غیرقابل تغییر اندازه با جهت یا نسبت ابعاد ثابت، در کد، حالت سازگاری اندازه (SCM) نامیده میشود. این شرط در ActivityRecord#shouldUseSizeCompatMode() تعریف شده است. هنگامی که یک فعالیت SCM راهاندازی میشود، پیکربندی مربوط به صفحه نمایش (مانند اندازه یا تراکم) در پیکربندی لغو درخواستی ثابت میشود، بنابراین فعالیت دیگر به پیکربندی نمایش فعلی وابسته نیست.
اگر فعالیت SCM نتواند کل صفحه را پر کند، از بالا تراز شده و به صورت افقی در مرکز قرار میگیرد. مرزهای فعالیت توسط AppWindowToken#calculateCompatBoundsTransformation() محاسبه میشوند.
وقتی یک فعالیت SCM از پیکربندی صفحه نمایش متفاوتی نسبت به کانتینر خود استفاده میکند (برای مثال، اندازه صفحه نمایش تغییر میکند یا فعالیت به صفحه نمایش دیگری منتقل میشود)، ActivityRecord#inSizeCompatMode() مقدار true میگیرد و SizeCompatModeActivityController (در رابط کاربری سیستم) فراخوانی برای نمایش دکمه راهاندازی مجدد فرآیند را دریافت میکند.
اندازهها و نسبتهای ابعاد نمایشگر
اندروید ۱۰ از نسبتهای ابعاد جدید، از نسبتهای بالای صفحه نمایشهای بلند و باریک گرفته تا نسبتهای ۱:۱، پشتیبانی میکند. برنامهها میتوانند ApplicationInfo#maxAspectRatio و ApplicationInfo#minAspectRatio را برای صفحه نمایشی که قادر به مدیریت آن هستند، تعریف کنند.

شکل 1. نمونه نسبتهای پشتیبانیشده در اندروید 10 برای برنامهها
پیادهسازیهای دستگاه میتوانند نمایشگرهای ثانویه با اندازهها و وضوحهای کوچکتر از اندازههای مورد نیاز اندروید ۹ و پایینتر (حداقل عرض یا ارتفاع ۲.۵ اینچ، حداقل ۳۲۰ DP برای smallestScreenWidth ) داشته باشند، اما فقط فعالیتهایی که از این نمایشگرهای کوچک پشتیبانی میکنند، میتوانند در آنجا قرار گیرند.
برنامهها میتوانند با اعلام حداقل اندازه پشتیبانیشده که کوچکتر یا مساوی اندازه نمایشگر هدف است، در این امر مشارکت کنند. برای این کار از ویژگیهای طرحبندی فعالیت android:minHeight و android:minWidth در AndroidManifest استفاده کنید.
سیاستهای نمایش
اندروید ۱۰ برخی از سیاستهای نمایش را از پیادهسازی پیشفرض WindowManagerPolicy در PhoneWindowManager جدا کرده و به کلاسهای مخصوص هر نمایشگر منتقل میکند، مانند:
- نمایش حالت و چرخش
- برخی از کلیدها و ردیابی رویداد حرکت
- رابط کاربری سیستم و پنجرههای تزئینی
در اندروید ۹ (و پایینتر)، کلاس PhoneWindowManager سیاستهای نمایش، وضعیت و تنظیمات، چرخش، ردیابی قاب پنجره تزئینی و موارد دیگر را مدیریت میکرد. اندروید ۱۰ بیشتر این موارد را به کلاس DisplayPolicy منتقل کرده است، به جز ردیابی چرخش که به DisplayRotation منتقل شده است.
نمایش تنظیمات پنجره
در اندروید ۱۰، تنظیمات پنجرهبندی قابل تنظیم برای هر نمایشگر گسترش یافته و شامل موارد زیر میشود:
- حالت پنجرهبندی پیشفرض نمایش
- مقادیر بیش از حد اسکن
- چرخش کاربر و حالت چرخش
- اندازه، چگالی و حالت مقیاسبندی اجباری
- حالت حذف محتوا (هنگامی که صفحه نمایش حذف میشود)
- پشتیبانی از تزئینات سیستم و IME
کلاس DisplayWindowSettings شامل تنظیماتی برای این گزینهها است. هر بار که تنظیماتی تغییر میکند، این تنظیمات در پارتیشن /data در display_settings.xml روی دیسک ذخیره میشوند. برای جزئیات بیشتر، به DisplayWindowSettings.AtomicFileStorage و DisplayWindowSettings#writeSettings() مراجعه کنید. تولیدکنندگان دستگاه میتوانند مقادیر پیشفرض را در display_settings.xml برای پیکربندی دستگاه خود ارائه دهند. با این حال، از آنجا که فایل در /data ذخیره میشود، ممکن است برای بازیابی فایل در صورت پاک شدن توسط پاکسازی، به منطق اضافی نیاز باشد.
به طور پیشفرض، اندروید ۱۰ هنگام ذخیره تنظیمات، DisplayInfo#uniqueId به عنوان شناسه نمایشگر استفاده میکند. uniqueId باید برای همه نمایشگرها پر شود. علاوه بر این، برای نمایشگرهای فیزیکی و شبکهای پایدار است. همچنین میتوان از پورت یک نمایشگر فیزیکی به عنوان شناسه استفاده کرد که میتوان آن را در DisplayWindowSettings#mIdentifier تنظیم کرد. پس از هر بار نوشتن، همه تنظیمات نوشته میشوند، بنابراین بهروزرسانی کلیدی که برای ورودی نمایشگر در حافظه استفاده میشود، ایمن است. برای جزئیات بیشتر، به شناسههای نمایشگر استاتیک مراجعه کنید.
تنظیمات به دلایل تاریخی در دایرکتوری /data ذخیره میشوند. در ابتدا، از آنها برای ذخیره تنظیمات تنظیم شده توسط کاربر، مانند چرخش صفحه نمایش، استفاده میشد.
شناسههای نمایش استاتیک
اندروید ۹ (و پایینتر) شناسههای پایداری برای نمایشگرها در چارچوب ارائه نمیکرد. وقتی یک نمایشگر به سیستم اضافه میشد، Display#mDisplayId یا DisplayInfo#displayId با افزایش یک شمارنده استاتیک برای آن نمایشگر ایجاد میشد. اگر سیستم نمایشگر یکسانی را اضافه و حذف میکرد، شناسه متفاوتی حاصل میشد.
اگر دستگاهی از زمان بوت چندین نمایشگر در دسترس داشت، بسته به زمانبندی، میتوانست به نمایشگرها شناسههای مختلفی اختصاص داده شود. در حالی که اندروید ۹ (و قبل از آن) شامل DisplayInfo#uniqueId بود، اطلاعات کافی برای تمایز بین نمایشگرها را در بر نمیگرفت زیرا نمایشگرهای فیزیکی به صورت local:0 یا local:1 شناسایی میشدند که نشاندهنده نمایشگر داخلی و خارجی بود.
اندروید ۱۰، DisplayInfo#uniqueId تغییر میدهد تا یک شناسه پایدار اضافه کند و بین نمایشگرهای محلی، شبکهای و مجازی تمایز قائل شود.
| نوع نمایشگر | قالب |
|---|---|
| محلی | local:<stable-id> |
| شبکه | network:<mac-address> |
| مجازی | virtual:<package-name-and-name> |
علاوه بر بهروزرسانیهای uniqueId ، DisplayInfo.address شامل DisplayAddress ، یک شناسه نمایش است که در طول راهاندازی مجدد پایدار است. در اندروید 10، DisplayAddress از نمایشگرهای فیزیکی و شبکهای پشتیبانی میکند. DisplayAddress.Physical شامل یک شناسه نمایش پایدار (مشابه uniqueId ) است و میتواند با DisplayAddress#fromPhysicalDisplayId() ایجاد شود.
اندروید ۱۰ همچنین یک روش مناسب برای دریافت اطلاعات پورت ( Physical#getPort() ) ارائه میدهد. این روش میتواند در چارچوب برای شناسایی استاتیک نمایشگرها استفاده شود. به عنوان مثال، در DisplayWindowSettings استفاده میشود. DisplayAddress.Network حاوی آدرس MAC است و میتواند با DisplayAddress#fromMacAddress() ایجاد شود.
این افزونهها به تولیدکنندگان دستگاه اجازه میدهند نمایشگرها را در تنظیمات چند نمایشگری ایستا شناسایی کنند و تنظیمات و ویژگیهای مختلف سیستم را با استفاده از شناسههای نمایشگر ایستا، مانند پورتها برای نمایشگرهای فیزیکی، پیکربندی کنند. این روشها پنهان هستند و فقط برای استفاده در system_server در نظر گرفته شدهاند.
با توجه به یک شناسه نمایش HWC (که میتواند مبهم و همیشه پایدار نباشد)، این روش شماره پورت ۸ بیتی (مختص پلتفرم) را که یک کانکتور فیزیکی برای خروجی نمایشگر را مشخص میکند، و همچنین حباب EDID نمایشگر را برمیگرداند. SurfaceFlinger اطلاعات سازنده یا مدل را از EDID استخراج میکند تا شناسههای نمایش ۶۴ بیتی پایدار را که در معرض چارچوب قرار میگیرند، تولید کند. اگر این روش پشتیبانی نشود یا خطا داشته باشد، SurfaceFlinger به حالت MD قدیمی برمیگردد، که در آن DisplayInfo#address برابر با تهی و DisplayInfo#uniqueId برابر با کدنویسی سخت است، همانطور که در بالا توضیح داده شد.
برای تأیید پشتیبانی از این ویژگی، دستور زیر را اجرا کنید:
$ 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"
استفاده از بیش از دو نمایشگر
در اندروید ۹ (و پایینتر)، SurfaceFlinger و DisplayManagerService وجود حداکثر دو نمایشگر فیزیکی با شناسههای ثابت ۰ و ۱ را فرض کردند.
با شروع از اندروید ۱۰، SurfaceFlinger میتواند از یک رابط برنامهنویسی سختافزاری (HWC) برای تولید شناسههای نمایشگر پایدار استفاده کند، که به آن امکان میدهد تعداد دلخواهی از نمایشگرهای فیزیکی را مدیریت کند. برای کسب اطلاعات بیشتر، به شناسههای نمایشگر استاتیک مراجعه کنید.
این چارچوب میتواند پس از دریافت شناسه نمایش ۶۴ بیتی از SurfaceControl#getPhysicalDisplayIds یا از رویداد hotplug DisplayEventReceiver ، از طریق SurfaceControl#getPhysicalDisplayToken به دنبال توکن IBinder برای یک نمایشگر فیزیکی بگردد.
در اندروید ۱۰ (و پایینتر)، نمایشگر داخلی اصلی TYPE_INTERNAL است و تمام نمایشگرهای ثانویه صرف نظر از نوع اتصال، به عنوان TYPE_EXTERNAL علامتگذاری میشوند. بنابراین، نمایشگرهای داخلی اضافی به عنوان خارجی در نظر گرفته میشوند. به عنوان یک راه حل، اگر HWC شناخته شده باشد و منطق تخصیص پورت قابل پیشبینی باشد، کد مختص دستگاه میتواند فرضیاتی در مورد DisplayAddress.Physical#getPort ایجاد کند.
این محدودیت در اندروید ۱۱ (و بالاتر) حذف شده است.
- در اندروید ۱۱، اولین نمایشگری که هنگام بوت شدن گزارش میشود، نمایشگر اصلی است. نوع اتصال (داخلی در مقابل خارجی) اهمیتی ندارد. با این حال، همچنان درست است که نمایشگر اصلی را نمیتوان جدا کرد و در عمل باید یک نمایشگر داخلی باشد. توجه داشته باشید که برخی از گوشیهای تاشو دارای چندین نمایشگر داخلی هستند.
- نمایشگرهای ثانویه بسته به نوع اتصالشان، به درستی به عنوان
Display.TYPE_INTERNALیاDisplay.TYPE_EXTERNAL(که قبلاً به ترتیب با نامهایDisplay.TYPE_BUILT_INوDisplay.TYPE_HDMIشناخته میشدند) طبقهبندی میشوند.
پیادهسازی
در اندروید ۹ و پایینتر، نمایشگرها با شناسههای ۳۲ بیتی شناسایی میشوند، که در آن ۰ نمایشگر داخلی، ۱ نمایشگر خارجی، [2, INT32_MAX] نمایشگرهای مجازی HWC هستند و -۱ نشان دهنده یک نمایشگر نامعتبر یا یک نمایشگر مجازی غیر HWC است.
از اندروید ۱۰ به بعد، به نمایشگرها شناسههای پایدار و مداوم داده میشود که به SurfaceFlinger و DisplayManagerService اجازه میدهد تا بیش از دو نمایشگر را ردیابی کرده و نمایشگرهای قبلاً دیده شده را تشخیص دهند. اگر HWC از IComposerClient.getDisplayIdentificationData پشتیبانی کند و دادههای شناسایی نمایشگر را ارائه دهد، SurfaceFlinger ساختار EDID را تجزیه کرده و شناسههای نمایشگر ۶۴ بیتی پایدار را برای نمایشگرهای فیزیکی و مجازی HWC اختصاص میدهد. شناسهها با استفاده از یک نوع گزینه بیان میشوند، که در آن مقدار null نشان دهنده یک نمایشگر نامعتبر یا نمایشگر مجازی غیر HWC است. بدون پشتیبانی HWC، SurfaceFlinger به رفتار قدیمی با حداکثر دو نمایشگر فیزیکی بازمیگردد.
تمرکز بر هر نمایشگر
برای پشتیبانی از چندین منبع ورودی که همزمان نمایشگرهای جداگانه را هدف قرار میدهند، اندروید ۱۰ را میتوان طوری پیکربندی کرد که از چندین پنجره متمرکز، حداکثر یک پنجره در هر نمایشگر، پشتیبانی کند. این قابلیت فقط برای انواع خاصی از دستگاهها در نظر گرفته شده است، زمانی که چندین کاربر همزمان با یک دستگاه تعامل دارند و از روشها یا دستگاههای ورودی مختلف، مانند اندروید اتوموتیو، استفاده میکنند.
اکیداً توصیه میشود که این ویژگی برای دستگاههای معمولی، از جمله دستگاههای چندصفحهای یا دستگاههایی که برای تجربیات شبیه دسکتاپ استفاده میشوند، فعال نشود . این امر عمدتاً به دلیل نگرانی امنیتی است که ممکن است باعث شود کاربران از خود بپرسند کدام پنجره دارای فوکوس ورودی است.
تصور کنید کاربری اطلاعات امن را در یک فیلد ورودی متن وارد میکند، مثلاً وارد یک برنامه بانکی میشود یا متنی را وارد میکند که حاوی اطلاعات حساس است. یک برنامه مخرب میتواند یک صفحه نمایش مجازی خارج از صفحه ایجاد کند که با آن یک فعالیت را اجرا کند، آن هم با یک فیلد ورودی متن. فعالیتهای مشروع و مخرب دارای فوکوس هستند و هر دو یک نشانگر ورودی فعال (مکاننمای چشمکزن) را نمایش میدهند.
با این حال، از آنجا که ورودی از یک صفحه کلید (سختافزار یا نرمافزار) فقط به بالاترین فعالیت (آن برنامهای که اخیراً اجرا شده است) وارد میشود، با ایجاد یک صفحه نمایش مجازی پنهان، یک برنامه مخرب میتواند ورودی کاربر را حتی هنگام استفاده از یک صفحه کلید نرمافزاری روی صفحه نمایش اصلی دستگاه، دریافت کند.
برای تنظیم فوکوس به ازای هر نمایشگر، com.android.internal.R.bool.config_perDisplayFocusEnabled استفاده کنید.
سازگاری
مشکل: در اندروید ۹ و پایینتر، حداکثر یک پنجره در سیستم در یک زمان دارای فوکوس است.
راه حل: در موارد نادری که دو پنجره از یک فرآیند فوکوس میشوند، سیستم فقط به پنجرهای که در مرتبه Z بالاتر است فوکوس میکند. این محدودیت برای برنامههایی که اندروید ۱۰ را هدف قرار میدهند حذف شده است، و در این مرحله انتظار میرود که بتوانند از فوکوس همزمان روی چندین پنجره پشتیبانی کنند.
پیادهسازی
WindowManagerService#mPerDisplayFocusEnabled در دسترس بودن این ویژگی را کنترل میکند. در ActivityManager ، اکنون ActivityDisplay#getFocusedStack() به جای ردیابی سراسری در یک متغیر استفاده میشود. ActivityDisplay#getFocusedStack() به جای ذخیره مقدار، تمرکز را بر اساس Z-order تعیین میکند. این به این دلیل است که فقط یک منبع، WindowManager، نیاز به ردیابی Z-order فعالیتها دارد.
ActivityStackSupervisor#getTopDisplayFocusedStack() برای مواردی که باید بالاترین پشته متمرکز در سیستم شناسایی شود، رویکرد مشابهی را اتخاذ میکند. پشتهها از بالا به پایین پیمایش میشوند و اولین پشته واجد شرایط جستجو میشود.
InputDispatcher اکنون میتواند چندین پنجرهی متمرکز (یکی برای هر نمایشگر) داشته باشد. اگر یک رویداد ورودی مختص نمایشگر باشد، به پنجرهی متمرکز در نمایشگر مربوطه ارسال میشود. در غیر این صورت، به پنجرهی متمرکز در نمایشگر متمرکز ارسال میشود، یعنی نمایشگری که کاربر اخیراً با آن تعامل داشته است.
به InputDispatcher::mFocusedWindowHandlesByDisplay و InputDispatcher::setFocusedDisplay() مراجعه کنید. برنامههای متمرکز شده همچنین به طور جداگانه در InputManagerService از طریق NativeInputManager::setFocusedApplication() بهروزرسانی میشوند.
در WindowManager ، پنجرههای متمرکز نیز به طور جداگانه ردیابی میشوند. به DisplayContent#mCurrentFocus و DisplayContent#mFocusedApp و کاربردهای مربوطه مراجعه کنید. روشهای ردیابی و بهروزرسانی فوکوس مرتبط از WindowManagerService به DisplayContent منتقل شدهاند.