برنامه خود را پاسخگو نگه دارید

شکل ۱. یک پنجره‌ی ANR که به کاربر نمایش داده می‌شود.

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

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

محرک‌های ANR

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

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

در اندروید، واکنش‌گرایی برنامه توسط سرویس‌های سیستمی ActivityManager و WindowManager کنترل می‌شود. اندروید زمانی که یکی از شرایط زیر را تشخیص دهد، کادر محاوره‌ای ANR را برای یک برنامه نمایش می‌دهد:

  • هیچ پاسخی به یک رویداد ورودی - مانند فشردن کلید یا لمس صفحه - در عرض ۵ ثانیه داده نمی‌شود.
  • یک BroadcastReceiver اجرای خود را ظرف 10 تا 20 ثانیه، برای اهداف پیش‌زمینه، به پایان نمی‌رساند. برای اطلاعات بیشتر، به بخش «زمان انتظار گیرنده پخش» مراجعه کنید.

از ANRها اجتناب کنید

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

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

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

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

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

    • هنگام اشتراک‌گذاری مخزن‌های نخ (thread pools) بین اجزا، مراقب باشید. از نخ‌های یکسان برای عملیات‌هایی که احتمالاً نیاز به زمان طولانی دارند و وظایف حساس به زمان مانند دریافت اعلان (broadcast recipients) استفاده نکنید.

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

  • اگر از BroadcastReceiver استفاده می‌کنید، اجرای broadcast receiver ها را در یک thread غیر اصلی با استفاده از Context.registerReceiver در نظر بگیرید. برای اطلاعات بیشتر، به ANR ها در BroadcastReceiver مراجعه کنید.

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

ANRها در گیرنده پخش

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

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

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

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

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

  • مخصوصاً برای بازی‌ها، محاسبات مربوط به حرکات را در یک کوروتین پس‌زمینه یا نخ کارگر انجام دهید.

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

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