برنامه را واکنش‌گرا نگه دارید

شکل ۱. کادر گفتگوی ANR به کاربر نشان داده شد.

این سند توضیح می‌دهد که سیستم Android چگونه تشخیص می‌دهد که برنامه‌ای پاسخگو نیست و نشان می‌دهد که چگونه برنامه‌تان را پاسخگو نگه دارید.

مهم نیست کد شما چقدر خوب نوشته شده باشد، ممکن است برنامه‌تان همچنان کند به‌نظر برسد، هنگ کند، برای مدت طولانی ثابت بماند، یا پردازش ورودی آن خیلی طول بکشد. اگر برنامه‌تان در پیش‌زمینه باشد و پاسخ ندهد، کاربر کادر گفتگوی «برنامه پاسخ نمی‌دهد» (ANR) را دریافت می‌کند، همان‌طور که در شکل ۱ نشان داده شده است. کادر گفتگوی «برنامه پاسخ نمی‌دهد» به کاربر اجازه می‌دهد برنامه را به‌اجبار ببندد. اگر برنامه در پیش‌زمینه نباشد، بدون اطلاع کاربر متوقف می‌شود. طراحی پاسخ‌گویی در برنامه برای به‌حداقل رساندن کادرهای گفتگوی ANR بسیار مهم است.

راه‌اندازهای ANR

به‌طور کلی، اگر برنامه‌ای نتواند به ورودی کاربر در رشته اصلی (که به‌عنوان رشته میانای کاربر نیز شناخته می‌شود) پاسخ دهد، سیستم ANR را نمایش می‌دهد و از پردازش رویدادهای ورودی کاربر توسط سیستم جلوگیری می‌کند.

برای مثال، اگر برنامه‌ای عملیات ورودی/خروجی مسدودکننده‌ای مثل دسترسی به شبکه را در رشته میانای کاربر انجام دهد، ممکن است ANR رخ دهد. مثال دیگر زمانی است که برنامه‌ای زمان زیادی را صرف ساختن ساختار پیچیده درون‌حافظه‌ای یا محاسبه حرکت بعدی در بازی در رشته میانای کاربر می‌کند.

در Android، پاسخ‌گویی برنامه توسط سرویس‌های سیستم ActivityManager و WindowManager نظارت می‌شود. Android کادر گفتگوی ANR را برای برنامه نمایش می‌دهد وقتی یکی از شرایط زیر را تشخیص دهد:

اجتناب از خطاهای ANR

در زیر نکته‌های کلی برای جلوگیری از خطاهای ANR آورده شده است. برای جزئیات بیشتر درباره تشخیص و اشکال‌زدایی انواع مختلف ANR، به صفحه‌های دیگر در این بخش مراجعه کنید.

  • رشته اصلی را همیشه بدون انسداد نگه دارید و از رشته‌ها به‌صورت استراتژیک استفاده کنید.

    • عملیات مسدودکننده یا طولانی‌مدت را در رشته اصلی برنامه انجام ندهید. درعوض، از روال‌های مشترک Kotlin برای انتقال کار به توزیع‌کننده‌های پس‌زمینه (مانند Dispatchers.IO یا Dispatchers.Default) استفاده کنید. از سازوکارهایی مانند viewModelScope برای راه‌اندازی ایمن این کارهای پس‌زمینه، یا LaunchedEffect برای راه‌اندازی آن‌ها در پاسخ به تغییرات حالت Compose استفاده کنید.

    • سعی کنید هرگونه رقابت قفل بین رشته اصلی و رشته‌های دیگر را به‌حداقل برسانید.

    • هرگونه کار غیرمرتبط با واسط کاربر را در رشته اصلی به‌حداقل برسانید، مثلاً هنگام رسیدگی به همه‌فرستی‌ها یا اجرای سرویس‌ها. هر روش یا تابعی که در رشته میانای کاربر اجرا می‌شود باید تا حد امکان کار کمی انجام دهد. به‌ویژه، فعالیت‌ها باید تا حد امکان کمترین کار را برای راه‌اندازی در روش‌های کلیدی چرخه حیات، مانند onCreate و onResume، انجام دهند. هرگز ورودی/خروجی یا محاسبات سنگین و مسدودکننده را مستقیماً در داخل تابع ترکیب‌پذیر انجام ندهید. این کار رشته میانای کاربر را درطول ترکیب‌بندی و ترکیب‌بندی مجدد مسدود می‌کند. برای کسب اطلاعات بیشتر درباره راه‌حل‌های موجود برای زمان‌بندی کار در رشته پس‌زمینه و برقراری ارتباط با واسط کاربر، به نمای کلی کارهای پس‌زمینه مراجعه کنید.

    • هنگام هم‌رسانی استخرهای رشته بین مؤلفه‌ها مراقب باشید. از رشته‌های یکسان برای عملیات‌های بالقوه طولانی‌مدت و کارهای حساس به زمان مانند دریافت پخش استفاده نکنید.

  • راه‌اندازی برنامه را سریع نگه دارید. عملیات‌های کند یا مسدودکننده را در کد راه‌اندازی برنامه به‌حداقل برسانید، مثلاً روش‌هایی که درطول راه‌اندازی تزریق وابستگی (مانند Hilt) اجرا می‌شوند یا مؤلفه‌هایی که بااستفاده از کتابخانه راه‌اندازی برنامه Jetpack مقداردهی اولیه می‌شوند. بااستفاده از Baseline Profiles، Startup Profiles، و R8 می‌توانید راه‌اندازی برنامه را بیشتر بهینه‌سازی کنید.

  • اگر از BroadcastReceiver استفاده می‌کنید، اجرای گیرنده‌های رویداد ثبت‌شده را در رشته‌ای غیراصلی بااستفاده از Context.registerReceiver درنظر بگیرید. برای اطلاعات بیشتر، خطاهای «برنامه پاسخ نمی‌دهد» در BroadcastReceiver را ببینید.

    • اگر از goAsync استفاده می‌کنید، مطمئن شوید PendingResult.finish به‌سرعت قبل‌از اتمام مهلت ANR فراخوانی شود.

خطاهای ANR در BroadcastReceiver

زمان اجرای BroadcastReceiver محدود است زیرا گیرنده‌های رویداد ثبت‌شده برای انجام کارهای کوچک و مجزا در پس‌زمینه طراحی شده‌اند، مانند ذخیره کردن تنظیمات یا ثبت کردن Notification. بنابراین، مانند سایر روش‌های فراخوانی‌شده در رشته میانای کاربر، برنامه‌ها باید از عملیات یا محاسبات طولانی‌مدت احتمالی در گیرنده رویداد ثبت‌شده اجتناب کنند. به‌جای انجام تکالیف طولانی‌مدت ازطریق رشته میانای کاربر، آن‌ها را در پس‌زمینه برای اجرای بعدی انجام دهید. برای اطلاعات بیشتر درباره راه‌حل‌های احتمالی، نمای کلی تکالیف پس‌زمینه را ببینید.

مشکل رایج دیگر با اشیاء BroadcastReceiver زمانی رخ می‌دهد که آن‌ها بیش‌ازحد اجرا شوند. اجرای مکرر در پس‌زمینه می‌تواند مقدار حافظه دردسترس برای برنامه‌های دیگر را کاهش دهد. برای اطلاعات بیشتر درباره نحوه فعال و غیرفعال کردن BroadcastReceiver اشیاء به‌طور مؤثر، به نمای کلی همه‌فرستی‌ها مراجعه کنید.

تقویت پاسخ‌گویی

به‌طور کلی، ۱۰۰ تا ۲۰۰ میلی‌ثانیه آستانه‌ای است که فراتر از آن کاربران متوجه کندی در برنامه می‌شوند. در اینجا چند نکته دیگر برای پاسخگو نشان دادن برنامه‌تان به کاربران آورده شده است:

  • اگر برنامه‌تان در پاسخ به ورودی کاربر درحال انجام کاری در پس‌زمینه است، نشان دهید که پیشرفت درحال انجام است، مثلاً با CircularProgressIndicator یا LinearProgressIndicator در میانای کاربری‌تان.

  • به‌ویژه برای بازی‌ها، محاسبات مربوط به حرکت‌ها را در یک روال هم‌زمان پس‌زمینه یا رشته عامل انجام دهید.

  • اگر برنامه‌تان مرحله راه‌اندازی اولیه زمان‌بری دارد، نمایش صفحه اسپلش یا رندر کردن ترکیب‌بندی اولیه را در سریع‌ترین زمان ممکن مدنظر قرار دهید. نشان دهید که بار کردن درحال انجام است و وضعیت واسط کاربر را به‌صورت ناهمگام پر کنید. در هر دو مورد، توصیه می‌کنیم به‌نحوی نشان دهید که پیشرفت درحال انجام است تا کاربر تصور نکند برنامه متوقف شده است.

  • از ابزارهای عملکرد مانند Perfetto و تحلیل‌گر واحد پردازش مرکزی برای تعیین گلوگاه‌ها در پاسخ‌گویی برنامه‌تان استفاده کنید.