اندروید ۱۱ (سطح API 30) یا بالاتر از قابلیت فریز کردن برنامههای ذخیرهشده در حافظه پنهان پشتیبانی میکند. این ویژگی با جلوگیری از عملکرد نادرست برنامههایی که ممکن است در حین ذخیره شدن در حافظه پنهان تلاش به اجرا داشته باشند، اجرای فرآیندهای ذخیرهشده در حافظه پنهان را متوقف کرده و میزان استفاده از منابع را کاهش میدهد.
قابلیت فریز کردن برنامههای ذخیرهشده، برنامهها را در حافظه رم نگه میدارد و در عین حال آنها را از پردازنده دور نگه میدارد. اگر اندروید تشخیص دهد که یک برنامه نباید کار کند اما ممکن است در آینده مورد نیاز باشد، به جای خاتمه دادن به آن، فرآیند برنامه را فریز میکند. این کار از شروع سرد (cold start) در زمانی که دوباره به برنامه نیاز است، جلوگیری میکند.
اندروید با انتقال فرآیندهای برنامههای ذخیرهشده در حافظه پنهان به یک گروه c منجمد، آنها را مسدود میکند. این کار باعث کاهش مصرف CPU فعال و غیرفعال در حضور برنامههای ذخیرهشده فعال میشود. میتوانید با استفاده از یک پرچم پیکربندی سیستم یا یک گزینه توسعهدهنده، مسدودکننده برنامه را فعال کنید.
در اندروید ۱۴ (سطح API ۳۴) و بالاتر، فریزر برنامههای ذخیرهشده شامل رفتارهای قوی زیر است:
- فرآیندهای برنامه در حالت ذخیره شده، 10 ثانیه پس از ورود به حالت ذخیره شده، مسدود میشوند.
- سیستم بلافاصله در طول یک رویداد چرخه عمر، فرآیند برنامهی قفلشده را از حالت قفلشده خارج میکند. این رویدادها شامل دریافت یک intent ، شروع یک سرویس کاری یا از سرگیری یک فعالیت توسط کاربر میشود.
ActivityManagerService تمام فرآیندهای برنامه را مدیریت میکند و تصمیمات مربوط به چرخه عمر برنامه را میگیرد. CachedAppOptimizer مسئول فریز کردن فرآیند برنامه است.
وقتی یک فرآیند برنامهای قفل میشود، تمام نخهای آن به حالت تعلیق در میآیند و تا زمانی که قفل نشوند، نمیتوانند کار CPU را انجام دهند. در نتیجه، برنامه نمیتواند جمعآوری زباله (GC) را انجام دهد و نمیتواند به رویدادهای اصلاح حافظه پاسخ دهد. برای اطلاعات بیشتر، به ComponentCallbacks2.onTrimMemory(int) مراجعه کنید. برای تطبیق با این مورد، از اندروید ۱۴ شروع کنید:
- برنامههایی که نمونه
Activityقابل مشاهدهای دارند، به محض انتقال به پسزمینه، ازTRIM_MEMORY_UI_HIDDENمطلع میشوند. برنامههایی که بدون UI در چرخه عمر باقی میمانند، مانند برنامههایی با سرویس پیشزمینه، ممکن استTRIM_MEMORY_BACKGROUNDدریافت کنند. سایر رویدادهای trim ارائه نمیشوند، زیرا وقتی برنامهها واجد شرایط آن رویدادها هستند، انتظار میرود که منجمد شوند. - کمی پس از ورود به حالت ذخیره شده، سیستم ممکن است از برنامه در زمان اجرا درخواست کند تا یک GC را برای آماده سازی جهت جلوگیری از مسدود شدن احتمالی انجام دهد.
- وقتی یک فرآیند برنامهای متوقف میشود، ممکن است مراحل فشردهسازی حافظه اضافی، مانند نوشتن صفحات کثیف در حافظه پشتیبان و تعویض صفحات ناشناس به ZRAM، رخ دهد.
- اگر تمام فرآیندهای یک برنامه خاص مسدود شوند، سیستم هرگونه سوکت TCP فعالی که توسط برنامه نگهداری میشود را خاتمه میدهد. این کار مانع از ارسال پینگهای TCP keepalive توسط سرور سوکت میشود که مودم دستگاه را از حالت آماده به کار خارج میکند.
فرآیندهای برنامه ذخیره شده در حافظه پنهان (Cache) زمانی که وضعیت فرآیند آنها از حالت ذخیره شده به حالت با اهمیت بالاتر ارتقا مییابد، از حالت انجماد خارج میشوند. برای کاهش رویدادهای انجمادزدایی در اندروید ۱۴ و بالاتر، سیستم، پخشهای ثبت شده در متن را در حالی که برنامه در حالت ذخیره شده است، در صف قرار میدهد. پخشهای ثبت شده در متن، گیرندههایی هستند که یک برنامه با فراخوانی Context.registerReceiver به صورت پویا ثبت میکند. سیستم این پخشهای صفبندی شده را تنها پس از انجمادزدایی برنامه ارائه میدهد. در مقابل، سیستم پخشهای اعلام شده در مانیفست را در صف قرار نمیدهد. پخشهای اعلام شده در مانیفست، گیرندههایی هستند که به صورت ایستا در AndroidManifest.xml با استفاده از عنصر <receiver> اعلام میشوند. سیستم بلافاصله برنامه ذخیره شده را برای ارائه پخشهای اعلام شده در مانیفست از حالت انجماد خارج میکند.
تأثیر بر سلامت سیستم
اگر تعداد فرآیندهای برنامه ذخیره شده در حافظه پنهان (cache) بیش از MAX_CACHED_PROCESSES باشد، اندروید فرآیند برنامه ذخیره شده با کمترین استفاده اخیر را خاتمه میدهد. در دستگاههای پشتیبانی شده که از اندروید ۱۴ یا بالاتر استفاده میکنند، MAX_CACHED_PROCESSES به طور قابل توجهی افزایش یافته است و به دستگاهها اجازه میدهد فرآیندهای برنامه ذخیره شده بیشتری را در رم نگه دارند.
نگهداری تعداد بیشتری از برنامهها در حافظه رم، تا 30 درصد کاهش در شروع سرد (clad start) را به همراه دارد، که این کاهشها بر اساس کل حافظه رم دستگاه تنظیم میشوند. در عین حال، مصرف CPU توسط برنامههای ذخیره شده در حافظه رم به حداقل میرسد و منجر به صرفهجویی قابل توجه در مصرف باتری میشود.
معافیتهای فریزر
تحت شرایط خاص، یک فرآیند برنامه ممکن است وارد حالت ذخیره شده در حافظه پنهان شود اما همچنان از حالت مسدود شده خارج بماند. این استثنائات جزئیات پیادهسازی هستند و ممکن است در نسخههای بعدی اندروید تغییر کنند:
- قفلهای فایل : اگر یک فرآیند ذخیرهشده در حافظه پنهان، قفل فایلی داشته باشد که سایر فرآیندهای ذخیرهنشده را مسدود میکند، فرآیندی که قفل را نگه داشته، مسدود نمیشود.
- پیوندهای
BIND_WAIVE_PRIORITY: فرآیندهای برنامه با پیوندهای ورودی ایجاد شده با استفاده ازContext.BIND_WAIVE_PRIORITYمیتوانند وارد حالت ذخیره شده شوند اما تا زمانی که همه فرآیندهای کلاینت متصل نیز ذخیره نشوند، غیرمتحرک باقی میمانند. این استثنا از برنامههای چند فرآیندی، مانند مرورگرهای وب با استفاده از برگههای سفارشی، پشتیبانی میکند.
پیادهسازی فریزر برنامهها
فریزر برنامههای ذخیرهشده از فریزر هسته cgroup v2 استفاده میکند. دستگاههایی که با هسته سازگار عرضه میشوند میتوانند آن را فعال کنند. گزینه توسعهدهنده Suspend execution for cached apps را فعال کنید یا پرچم پیکربندی دستگاه activity_manager_native_boot use_freezer را روی true تنظیم کنید. برای مثال:
adb shell device_config put activity_manager_native_boot use_freezer true && adb reboot وقتی پرچم use_freezer روی false تنظیم کنید یا گزینه توسعهدهنده را غیرفعال کنید، فریزر غیرفعال میشود. برای مثال:
adb shell device_config put activity_manager_native_boot use_freezer false && adb rebootمیتوانید با تغییر پیکربندی دستگاه در نسخه یا بهروزرسانی نرمافزار، این تنظیم را تغییر دهید.
برای لغو MAX_CACHED_PROCESSES ، به عنوان مثال، برای تنظیم مقدار روی ۱۰۲۴ برای آزمایش:
adb shell device_config put activity_manager max_cached_processes 1024adb shell device_config set_sync_disabled_for_tests persistent
برای برگرداندن لغو MAX_CACHED_PROCESSES :
adb shell device_config delete activity_manager max_cached_processesadb shell device_config set_sync_disabled_for_tests none
از اندروید ۱۶ (سطح API ۳۶) و بالاتر، اندروید APIهای عمومی رسمی مانند IBinder.FrozenStateChangeCallback و IBinder.addFrozenStateChangeCallback را ارائه میدهد تا مشاهده شود چه زمانی فرآیندهای از راه دور مسدود یا آزاد میشوند. کامپوننتهایی که با برنامههایی که ممکن است در حافظه پنهان ذخیره شوند، تعامل دارند، میتوانند از این APIها برای ردیابی وضعیت مسدود شدن فرآیندهای از راه دور استفاده کنند.
نیازمندیهای دستگاه و هسته
فریز کردن برنامههای ذخیرهشده نیاز به پشتیبانی از kernel cgroup v2 دارد. علاوه بر این، فریز کردن اعلانهای تغییر وضعیت با استفاده از IBinder.FrozenStateChangeCallback نیاز به پشتیبانی از درایور kernel binder دارد که در هستههای مشترک اندروید (ACK) و تصاویر هسته عمومی (GKI) از اندروید ۱۴ (سطح API ۳۴) و بالاتر استاندارد است.
شما میتوانید با استفاده از دستورات استاندارد adb ، به شرح زیر، تأیید کنید که آیا دستگاه از این قابلیتها پشتیبانی میکند یا خیر:
بررسی کنید که آیا فریزر روی دستگاه فعال است یا خیر (برای نسخههای کاربری یا اشکالزدایی) :
adb shell device_config get activity_manager_native_boot use_freezerیا تأیید کنید که فرآیندها به طور فعال توسط سیستم مسدود میشوند:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"پشتیبانی از کنترلر فریزر cgroup v2 را بررسی کنید (برای هر دستگاهی) :
تأیید کنید که
freezerدر بین کنترلرهای cgroup v2 موجود فهرست شده است:adb shell cat /sys/fs/cgroup/cgroup.controllersروش دیگر، در یک دستگاه روت شده یا نسخه ساخت userdebug، تأیید کنید که گره فریزر cgroup v2 در یک cgroup فرزند نصب شده است:
adb root && adb shell ls /sys/fs/cgroup/uid_0/cgroup.freezeاگر این فایل وجود داشته باشد، هسته از فریزر cgroup v2 پشتیبانی میکند.
پشتیبانی از اعلان تغییر وضعیت فریز (برای هر دستگاهی) را بررسی کنید :
در دستگاههایی که اندروید ۱۴ و بالاتر را اجرا میکنند و درایورهای اتصالدهندهی Generic Kernel Image (GKI) سازگار دارند،
IBinder.addFrozenStateChangeCallbackفراخوانیهای برگشتی را با موفقیت ثبت میکند. اگر درایور اتصالدهندهی هستهی اصلی از اعلانهای توقف پشتیبانی نکند، این متدUnsupportedOperationExceptionرا صادر میکند.
مدیریت ویژگیهای سفارشی
انتظار نمیرود فرآیندهای برنامه هنگام ذخیره شدن در حافظه پنهان کاری انجام دهند، اما برخی از برنامهها ممکن است دارای ویژگیهای سفارشی باشند که توسط فرآیندهایی پشتیبانی میشوند که انتظار میرود هنگام ذخیره شدن در حافظه پنهان اجرا شوند. وقتی قابلیت انجماد برنامهها در دستگاهی که چنین برنامههایی را اجرا میکند فعال باشد، فرآیندهای ذخیره شده در حافظه پنهان مسدود میشوند و ممکن است از کار کردن ویژگیهای سفارشی جلوگیری کنند.
به عنوان یک راه حل، میتوانید وضعیت فرآیند را قبل از اینکه نیاز به انجام کاری داشته باشد، به noncached تغییر دهید. این تغییر به برنامهها اجازه میدهد تا فعال بمانند. نمونههایی از وضعیتهای فعال شامل یک سرویس پیشزمینه محدود یا وضعیت پیشزمینه است.
حالتهای خرابی رایج
وقتی فرآیندهای برنامه هنگ میکنند، ارتباط بین فرآیندی (IPC) یا زمانبندی نامناسب وظایف میتواند منجر به خاتمه برنامه یا رفتار غیرمنتظره شود.
تراکنشهای اتصال همزمان به فرآیندهای منجمد
وقتی یک فرآیند برنامه کلاینت، یک تراکنش اتصال همزمان را به یک فرآیند برنامه سرور که مسدود شده است ارسال میکند، سیستم بلافاصله فرآیند برنامه سرور را خاتمه میدهد. این کار مانع از مسدود شدن نامحدود نخ کلاینت در حین انتظار برای پاسخ از سرور مسدود شده میشود. سپس نخ کلاینت RemoteException دریافت میکند و هر شنونده ثبت شدهای فعال میشود. برای اطلاعات بیشتر، به IBinder.linkToDeath مراجعه کنید.
علت اصلی : این خرابی معمولاً ناشی از یک اشکال در برنامه کلاینت است. هنگامی که یک کلاینت به یک سرویس متصل میشود، فرآیند سرور به کلاینت متصل میشود و از ورود به حالت کش شده قبل از کلاینت جلوگیری میشود. برای اطلاعات بیشتر، به Context.bindService مراجعه کنید. با این حال، هنگامی که کلاینت Context.unbindService را فراخوانی میکند، فرآیند سرور میتواند کش شده و منجمد شود. اگر کلاینت پس از unbinding به استفاده از مرجع IBinder کش شده ادامه دهد، خطر برقراری ارتباط با یک فرآیند منجمد وجود دارد.
برای جلوگیری از این مشکل، مطمئن شوید که برنامههای کلاینت بلافاصله پس از فراخوانی Context.unbindService ارجاعات IBinder را حذف میکنند.
مدیریت تماسهای برگشتی از راه دور به فرآیندهای کلاینت
سرویسها و اجزای سیستمی که فراخوانیهای binder با طول عمر بالا را برای فرآیندهای کلاینت حفظ میکنند، میتوانند با ردیابی وضعیت ثابت کلاینت، از خرابیهای همزمان و سرریزهای بافر غیرهمزمان جلوگیری کنند:
- ثبت یک شنونده تغییر وضعیت : از
IBinder.addFrozenStateChangeCallbackروی توکنهای binder کلاینت ورودی استفاده کنید تا هنگام ورود یا خروج فرآیند کلاینت از فریزر، اعلانهایی دریافت کنید. - مکث در ارسال در حین توقف : وقتی یک کلاینت
STATE_FROZENرا وارد میکند، ارسال فراخوانیهای برگشتی یا بهروزرسانیهای وضعیت برای آن کلاینت را متوقف میکند. - از سرگیری و تحویل پس از رفع انجماد : هنگامی که کلاینت به حالت
STATE_UNFROZENمنتقل میشود، ارسال پاسخ فراخوانی از سر گرفته میشود و هرگونه بهروزرسانی دستهای یا تجمیعی لازم ارائه میشود. - استفاده از RemoteCallbackList : سرویسهای سیستمی که از
RemoteCallbackListاستفاده میکنند میتوانند سیاستهای فراخوانیشدهی مسدود شده را طوری پیکربندی کنند که بهطور خودکار ارسال پاسخ به تماس را متوقف کرده و از سر بگیرند، بدون اینکه منطق ردیابی دستی حفظ شود. برای اطلاعات بیشتر، به توصیههای Binder freezer برای سرویسهای سیستمی مراجعه کنید.
سرریز بافر تراکنش اتصالدهنده ناهمزمان
وقتی یک فرآیند برنامهی سرور در حالت فریز شده، تراکنشهای اتصال ناهمزمان ( oneway ) دریافت میکند، این تراکنشها در یک بافر به ازای هر فرآیند بافر میشوند. اگر سرور در حالت فریز شده، تراکنشهای ناهمزمان زیادی دریافت کند، بافر سرریز میشود و سیستم، فرآیند برنامهی سرور را خاتمه میدهد.
برای جلوگیری از این سرریز بافر، از ارسال تراکنشهای اتصال ناهمزمان بیش از حد به فرآیندهایی که ممکن است ذخیره شده یا منجمد شده باشند، خودداری کنید.
اجرای مکرر وظایف زمانبندیشده پس از خارج شدن از حالت انجماد
اگر یک برنامه وظایف تکراری را اجرا کند، آنها در حالی که فرآیند متوقف میشود، به حالت تعلیق در میآیند. برای اطلاعات بیشتر، به ScheduledThreadPoolExecutor.scheduleAtFixedRate یا Timer.scheduleAtFixedRate مراجعه کنید. هنگامی که فرآیند از حالت توقف خارج میشود، اجراهای از دست رفته انباشته شده ممکن است به سرعت و تقریباً بدون هیچ تأخیری پشت سر هم اجرا شوند.
برای جلوگیری از افزایش ناگهانی تعداد اجراها هنگام از کار افتادن برنامه، برای وظایف پسزمینه به جای scheduleAtFixedRate از scheduleWithFixedDelay استفاده کنید. همچنین میتوانید WorkManager استفاده کنید.
فریزر برنامهها را آزمایش و عیبیابی کنید
برای تأیید عملکرد صحیح فریزر برنامه یا عیبیابی مشکلات مربوط به فریزر، از ابزارها و دستورات تشخیصی زیر استفاده کنید:
دستورات مدیریت فعالیت
شما میتوانید از دستورات adb shell am برای کنترل دستی فریز کردن و فشردهسازی برای یک فرآیند خاص استفاده کنید:
مجبور کردن یک فرآیند به فریز کردن:
adb shell am freeze <process>مجبور کردن یک فرآیند به خارج شدن از حالت فریز:
adb shell am unfreeze <process>فشردهسازی کامل حافظه را روی یک فرآیند اعمال کنید:
adb shell am compact full <process>
معاینه لاگات
برای مشاهدهی ورودیهای فریز شده و نشده در هر بار مهاجرت یک فرآیند به داخل یا خارج از فریز، دستور logcat را اجرا کنید:
adb logcat | grep -i "\(freezing\|froze\)"لاگهای دلیلِ رفع انجماد، مقادیر شمارششده را از بافر شمارشی پروتکل UnfreezeReason خروجی میدهند.
بازرسی دامپسی
با استفاده از dumpsys activity لیستی از فرآیندهای مسدود شده را بررسی کنید:
adb shell dumpsys activity | grep -A 20 "Apps frozen:"وجود فایل /sys/fs/cgroup/uid_0/cgroup.freeze را بررسی کنید.
اطلاعات خروج از برنامه
برای جستجوی دلیل خاتمه یک فرآیند قبلی، به ActivityManager.getHistoricalProcessExitReasons مراجعه کنید. اگر یک فرآیند برنامه به دلیل مشکلی مرتبط با فریز شدن، مانند دریافت یک تراکنش binder همزمان در حین فریز شدن، خاتمه یافته باشد، دلیل خروج روی ApplicationExitInfo.REASON_FREEZER تنظیم میشود.
ردیابی کامل
رویدادهای مرتبط با فریزر در مسیرهای Perfetto به مسیری به نام Freezer تحت فرآیند system_server ارسال میشوند:
- برشهای
FreezeوUnfreezeنشان میدهند که چه زمانی یک فرآیند تغییر حالت میدهد. - رویدادهای
updateAppFreezeStateLSPنشان میدهند که چه زمانی سرور سیستم، ویژگیهای فرآیند را برای تصمیمگیری در مورد فریز کردن یا آزاد کردن فریز، دوباره بررسی میکند.
شما میتوانید این رویدادها را مستقیماً در رابط کاربری Perfetto بررسی کنید یا آنها را با استفاده از PerfettoSQL تجزیه و تحلیل کنید:
INCLUDE PERFETTO MODULE slices.with_context;
SELECT *
FROM process_slice
WHERE process_name = "system_server"
AND track_name = "Freezer"
AND (name LIKE "Freeze %" OR name LIKE "Unfreeze %");
در کتابخانه استاندارد PerfettoSQL، رویدادهای freezer نیز در جدول android_freezer_events خلاصه میشوند.