زمان راه اندازی اپلیکیشن

کاربران انتظار دارند برنامه‌ها سریع بارگذاری شوند و واکنش‌گرا باشند. برنامه‌ای با زمان شروع کند این انتظار را برآورده نمی‌کند و می‌تواند کاربران را ناامید کند. این نوع تجربه ضعیف می‌تواند باعث شود کاربر به برنامه شما در فروشگاه Play امتیاز ضعیفی بدهد یا حتی برنامه شما را به طور کلی کنار بگذارد.

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

درک حالت‌های مختلف شروع برنامه

اجرای برنامه می‌تواند در یکی از سه حالت زیر انجام شود: شروع سرد، شروع گرم یا شروع داغ. هر حالت بر مدت زمان قابل مشاهده شدن برنامه شما برای کاربر تأثیر می‌گذارد. در حالت شروع سرد، برنامه شما از ابتدا شروع می‌شود. در حالت‌های دیگر، سیستم باید برنامه در حال اجرا را از پس‌زمینه به پیش‌زمینه بیاورد.

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

برای بهینه‌سازی برنامه خود برای راه‌اندازی سریع، درک آنچه در سطح سیستم و برنامه اتفاق می‌افتد و نحوه تعامل آنها در هر یک از این حالت‌ها مفید است.

دو معیار مهم برای تعیین زمان شروع به کار برنامه ، زمان نمایش اولیه (TTID) و زمان ترسیم کامل (TTFD) هستند. TTID مدت زمانی است که طول می‌کشد تا اولین فریم نمایش داده شود و TTFD مدت زمانی است که طول می‌کشد تا برنامه کاملاً تعاملی شود. هر دو به یک اندازه مهم هستند، زیرا TTID به کاربر اطلاع می‌دهد که برنامه در حال بارگیری است و TTFD زمانی است که برنامه واقعاً قابل استفاده است. اگر هر یک از این موارد خیلی طولانی باشد، ممکن است کاربر قبل از بارگیری کامل، از برنامه شما خارج شود.

استارت سرد

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

این نوع شروع، بزرگترین چالش را برای به حداقل رساندن زمان راه‌اندازی ارائه می‌دهد، زیرا سیستم و برنامه نسبت به سایر حالت‌های راه‌اندازی، کار بیشتری برای انجام دادن دارند.

در ابتدای شروع سرد، سیستم سه وظیفه زیر را بر عهده دارد:

  1. برنامه را بارگیری و اجرا کنید.
  2. بلافاصله پس از اجرا، یک پنجره شروع خالی برای برنامه نمایش داده می‌شود.
  3. فرآیند برنامه را ایجاد کنید.

به محض اینکه سیستم فرآیند برنامه را ایجاد کرد، فرآیند برنامه مسئول مراحل بعدی است:

  1. شیء app را ایجاد کنید.
  2. تاپیک اصلی رو راه اندازی کنید.
  3. فعالیت اصلی را ایجاد کنید.
  4. بازدیدها را افزایش دهید.
  5. صفحه نمایش را چیدمان کنید.
  6. قرعه کشی اولیه را انجام دهید.

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

شکل ۱ نحوه‌ی تعامل فرآیندهای سیستم و برنامه را بین یکدیگر نشان می‌دهد.

شکل ۱. نمایش بصری بخش‌های مهم راه‌اندازی سرد یک اپلیکیشن.

مشکلات عملکردی می‌توانند در طول ایجاد برنامه و ایجاد فعالیت ایجاد شوند.

ایجاد برنامه

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

اگر Application.onCreate() در برنامه خود override کنید، سیستم متد onCreate() را روی شیء برنامه شما فراخوانی می‌کند. پس از آن، برنامه، نخ اصلی، که به عنوان نخ UI نیز شناخته می‌شود، را ایجاد می‌کند و آن را موظف به ایجاد فعالیت اصلی شما می‌کند.

از این نقطه، فرآیندهای سطح سیستم و برنامه مطابق با مراحل چرخه حیات برنامه پیش می‌روند.

ایجاد فعالیت

پس از اینکه فرآیند برنامه، اکتیویتی شما را ایجاد کرد، اکتیویتی عملیات زیر را انجام می‌دهد:

  1. مقادیر را مقداردهی اولیه می‌کند.
  2. سازنده‌ها را فراخوانی می‌کند.
  3. متد callback، مانند Activity.onCreate() ، را متناسب با وضعیت چرخه حیات فعلی اکتیویتی فراخوانی می‌کند.

معمولاً متد onCreate() بیشترین تأثیر را بر زمان بارگذاری دارد، زیرا کاری را با بالاترین سربار انجام می‌دهد: بارگذاری و inflate کردن viewها و مقداردهی اولیه اشیاء مورد نیاز برای اجرای activity.

شروع گرم

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

  • کاربر از برنامه شما خارج می‌شود اما سپس دوباره آن را اجرا می‌کند. این فرآیند ممکن است به اجرا ادامه دهد، اما برنامه باید با استفاده از فراخوانی onCreate() اکتیویتی را از ابتدا بازسازی کند.

  • سیستم برنامه شما را از حافظه خارج می‌کند و سپس کاربر آن را دوباره اجرا می‌کند. فرآیند و فعالیت باید مجدداً راه‌اندازی شوند، اما این وظیفه می‌تواند تا حدودی از بسته وضعیت نمونه ذخیره شده که به onCreate() ارسال می‌شود، بهره‌مند شود.

شروع داغ

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

با این حال، اگر مقداری از حافظه در پاسخ به رویدادهای اصلاح حافظه، مانند onTrimMemory() ، پاک شود، این اشیاء باید در پاسخ به رویداد شروع سریع (hot start) دوباره ایجاد شوند.

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

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

نحوه شناسایی شروع برنامه در Perfetto

برای اشکال‌زدایی مشکلات راه‌اندازی برنامه، تعیین اینکه دقیقاً چه چیزی در مرحله راه‌اندازی برنامه گنجانده شده است، مفید است. برای شناسایی کل مرحله راه‌اندازی برنامه در Perfetto ، این مراحل را دنبال کنید:

  1. در Perfetto، ردیفی را که معیار مشتق‌شده از Android App Startups را دارد، پیدا کنید. اگر آن را نمی‌بینید، سعی کنید با استفاده از برنامه ردیابی سیستم روی دستگاه ، ردیابی انجام دهید.

    شکل ۳. برش متریک مشتق شده توسط استارتاپ‌های اپلیکیشن اندروید در Perfetto.
  2. روی برش مرتبط کلیک کنید و برای انتخاب برش، کلید m را فشار دهید. براکت‌ها در اطراف برش ظاهر می‌شوند و مدت زمان صرف شده را نشان می‌دهند. مدت زمان نیز در برگه انتخاب فعلی نشان داده شده است.

  3. با کلیک روی آیکون سنجاق، ردیف «راه‌اندازی برنامه‌های اندروید» را سنجاق کنید. این آیکون با نگه داشتن اشاره‌گر روی ردیف قابل مشاهده است.

  4. به ردیفی که برنامه مورد نظر در آن قرار دارد بروید و روی اولین سلول کلیک کنید تا ردیف باز شود.

  5. با فشار دادن w ، روی نخ اصلی، معمولاً در بالا، زوم کنید (برای کوچک‌نمایی، به ترتیب s، a، d را فشار دهید، به چپ و به راست حرکت کنید).

    شکل ۴. برش متریک مشتق‌شده از استارتاپ‌های برنامه اندروید در کنار نخ اصلی برنامه.
  6. برش معیارهای مشتق‌شده، مشاهده‌ی دقیق آنچه در راه‌اندازی برنامه گنجانده شده است را آسان‌تر می‌کند، بنابراین می‌توانید با جزئیات بیشتری به اشکال‌زدایی ادامه دهید.

از معیارها برای بررسی و بهبود استارتاپ‌ها استفاده کنید

برای تشخیص صحیح عملکرد زمان شروع، می‌توانید معیارهایی را که نشان می‌دهند برنامه شما چقدر طول می‌کشد تا شروع به کار کند، پیگیری کنید. اندروید ابزارهای مختلفی را برای نشان دادن مشکل برنامه شما ارائه می‌دهد و به شما در تشخیص آن کمک می‌کند. Android Vitals می‌تواند به شما هشدار دهد که مشکلی در حال رخ دادن است و ابزارهای تشخیصی می‌توانند به شما در تشخیص مشکل کمک کنند.

مزایای استفاده از معیارهای سنجش استارتاپ

اندروید از معیارهای زمان نمایش اولیه (TTID) و زمان نمایش کامل (TTFD) برای بهینه‌سازی راه‌اندازی سرد و گرم برنامه استفاده می‌کند. Android Runtime (ART) از داده‌های این معیارها برای پیش‌کامپایل کارآمد کد جهت بهینه‌سازی راه‌اندازی‌های آینده استفاده می‌کند.

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

نکات مهم اندروید

موارد حیاتی اندروید می‌توانند با هشدار دادن به شما در کنسول Play در صورت طولانی شدن زمان راه‌اندازی برنامه، به بهبود عملکرد برنامه شما کمک کنند.

Android Vitals زمان‌های راه‌اندازی زیر را برای برنامه شما بیش از حد در نظر می‌گیرد:

  • روشن شدن سرد ۵ ثانیه یا بیشتر طول می‌کشد.
  • روشن شدن گرم ۲ ثانیه یا بیشتر طول می‌کشد.
  • روشن شدن سریع ۱.۵ ثانیه یا بیشتر طول می‌کشد.

Android Vitals از معیار زمان نمایش اولیه (TTID) استفاده می‌کند. برای اطلاعات بیشتر در مورد نحوه جمع‌آوری داده‌های Android Vitals توسط Google Play، به مستندات Play Console مراجعه کنید.

زمان نمایش اولیه

زمان نمایش اولیه (TTID) مدت زمانی است که طول می‌کشد تا اولین فریم از رابط کاربری برنامه نمایش داده شود. این معیار، مدت زمانی را که طول می‌کشد تا یک برنامه اولین فریم خود را تولید کند، اندازه‌گیری می‌کند، از جمله مقداردهی اولیه فرآیند در طول شروع سرد، ایجاد فعالیت در طول شروع سرد یا گرم، و نمایش اولین فریم. پایین نگه داشتن TTID برنامه شما با اجازه دادن به کاربران برای مشاهده راه‌اندازی سریع برنامه، به بهبود تجربه کاربری کمک می‌کند. TTID به طور خودکار برای هر برنامه توسط چارچوب اندروید گزارش می‌شود. هنگام بهینه‌سازی برای شروع برنامه، توصیه می‌کنیم reportFullyDrawn برای دریافت اطلاعات تا TTFD پیاده‌سازی کنید.

TTID به عنوان یک مقدار زمانی اندازه‌گیری می‌شود که نشان دهنده کل زمان سپری شده است که شامل توالی رویدادهای زیر است:

  • راه‌اندازی فرآیند.
  • مقداردهی اولیه اشیاء.
  • ایجاد و مقداردهی اولیه فعالیت
  • باد کردن طرح.
  • نقاشی کشیدن در برنامه برای اولین بار.

بازیابی TTID

برای یافتن TTID، در ابزار خط فرمان Logcat به دنبال یک خط خروجی حاوی مقداری به نام Displayed بگردید. این مقدار TTID است و شبیه به مثال زیر است که در آن TTID برابر با 3s534ms است:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms

برای یافتن TTID در اندروید استودیو، فیلترها را در نمای Logcat خود از منوی کشویی فیلتر غیرفعال کنید و سپس زمان Displayed را پیدا کنید، همانطور که در شکل 5 نشان داده شده است. غیرفعال کردن فیلترها ضروری است زیرا سرور سیستم، نه خود برنامه، این گزارش را ارائه می‌دهد.

شکل ۵. فیلترهای غیرفعال و مقدار Displayed در logcat.

معیار Displayed در خروجی Logcat لزوماً مدت زمان لازم برای بارگذاری و نمایش همه منابع را نشان نمی‌دهد. این معیار، منابعی را که در فایل طرح‌بندی به آنها اشاره نشده یا برنامه به عنوان بخشی از مقداردهی اولیه شیء ایجاد می‌کند، حذف می‌کند. این معیار این منابع را حذف می‌کند زیرا بارگذاری آنها یک فرآیند درون‌خطی است و نمایش اولیه برنامه را مسدود نمی‌کند.

گاهی اوقات خط Displayed در خروجی Logcat شامل یک فیلد اضافی برای کل زمان است. برای مثال:

ActivityManager: Displayed com.android.myexample/.StartupTiming: +3s534ms (total +1m22s643ms)

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

توصیه می‌کنیم در اندروید استودیو از Logcat استفاده کنید، اما اگر از اندروید استودیو استفاده نمی‌کنید، می‌توانید با اجرای برنامه خود با دستور adb shell activity manager ، TTID را نیز اندازه‌گیری کنید. در اینجا مثالی آورده شده است:

adb [-d|-e|-s <serialNumber>] shell am start -S -W
com.example.app/.MainActivity
-c android.intent.category.LAUNCHER
-a android.intent.action.MAIN

معیار Displayed مانند قبل در خروجی Logcat ظاهر می‌شود. پنجره ترمینال شما موارد زیر را نمایش می‌دهد:

Starting: Intent
Activity: com.example.app/.MainActivity
ThisTime: 2044
TotalTime: 2044
WaitTime: 2054
Complete

آرگومان‌های -c و -a اختیاری هستند و به شما امکان می‌دهند <category> و <action> را مشخص کنید.

زمان نمایش کامل

زمان نمایش کامل (TTFD) زمانی است که طول می‌کشد تا یک برنامه برای کاربر تعاملی شود. این زمان به عنوان زمانی که طول می‌کشد تا اولین فریم از رابط کاربری برنامه نمایش داده شود، و همچنین محتوایی که پس از نمایش فریم اولیه به صورت غیرهمزمان بارگذاری می‌شود، گزارش می‌شود. به طور کلی، این محتوای اولیه‌ای است که از شبکه یا دیسک بارگذاری می‌شود، همانطور که توسط برنامه گزارش می‌شود. به عبارت دیگر، TTFD شامل TTID و همچنین زمانی است که طول می‌کشد تا برنامه قابل استفاده شود. پایین نگه داشتن TTFD برنامه شما با فراهم کردن امکان تعامل سریع کاربران با برنامه شما، به بهبود تجربه کاربری کمک می‌کند.

سیستم TTID را زمانی تعیین می‌کند که Choreographer متد onDraw() مربوط به activity را فراخوانی می‌کند و زمانی که می‌داند این فراخوانی برای اولین بار انجام می‌شود. با این حال، سیستم نمی‌داند چه زمانی TTFD را تعیین کند زیرا هر برنامه رفتار متفاوتی دارد. برای تعیین TTFD، برنامه باید زمانی که به حالت کاملاً ترسیم شده می‌رسد، به سیستم سیگنال دهد.

بازیابی TTFD

برای یافتن TTFD، با فراخوانی متد reportFullyDrawn() از ComponentActivity ، وضعیت رسم کامل را اعلام کنید. متد reportFullyDrawn گزارش می‌دهد که برنامه چه زمانی به طور کامل رسم شده و در حالت قابل استفاده قرار دارد. TTFD زمان سپری شده از زمانی است که سیستم، intent اجرای برنامه را دریافت می‌کند تا زمانی که reportFullyDrawn() فراخوانی می‌شود. اگر reportFullyDrawn() را فراخوانی نکنید، هیچ مقدار TTFD گزارش نمی‌شود.

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

وقتی از reportFullyDrawn() استفاده می‌کنید، Logcat خروجی مانند مثال زیر را نمایش می‌دهد که در آن TTFD برابر با 1s54ms است:

system_process I/ActivityManager: Fully drawn {package}/.MainActivity: +1s54ms

خروجی Logcat گاهی اوقات شامل total زمان است، همانطور که در بخش «زمان تا نمایش اولیه» بحث شده است.

اگر زمان نمایش شما کندتر از آن چیزی است که می‌خواهید، می‌توانید سعی کنید گلوگاه‌های فرآیند راه‌اندازی را شناسایی کنید.

در موارد اساسی که از دستیابی به حالت کاملاً ترسیم شده آگاه هستید، می‌توانید از reportFullyDrawn() برای اعلام وضعیت کاملاً ترسیم شده استفاده کنید. با این حال، در مواردی که نخ‌های پس‌زمینه باید قبل از دستیابی به حالت کاملاً ترسیم شده، کار پس‌زمینه را انجام دهند، برای اندازه‌گیری دقیق‌تر TTFD باید تابع reportFullyDrawn() به تأخیر بیندازید. برای یادگیری نحوه تأخیر reportFullyDrawn() ، به بخش زیر مراجعه کنید.

بهبود دقت زمان‌بندی راه‌اندازی

اگر برنامه شما بارگذاری تنبل (lazy loading) را انجام می‌دهد و نمایش اولیه شامل تمام منابع نمی‌شود، مانند زمانی که برنامه شما تصاویر را از شبکه دریافت می‌کند، ممکن است بخواهید فراخوانی تابع reportFullyDrawn را تا زمانی که برنامه شما قابل استفاده شود، به تعویق بیندازید تا بتوانید جمعیت لیست را به عنوان بخشی از زمان‌بندی معیار خود لحاظ کنید.

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

برای گنجاندن جمعیت لیست به عنوان بخشی از زمان‌بندی معیار خود، FullyDrawnReporter با استفاده از getFullyDrawnReporter() دریافت کنید و یک گزارشگر به آن در کد برنامه خود اضافه کنید. گزارشگر را پس از اتمام پر کردن لیست توسط وظیفه پس‌زمینه، رها کنید.

FullyDrawnReporter تا زمانی که همه گزارشگران اضافه شده آزاد نشوند، متد reportFullyDrawn() را فراخوانی نمی‌کند. با اضافه کردن یک گزارشگر تا زمان تکمیل فرآیند پس‌زمینه، زمان‌بندی‌ها شامل مقدار زمانی که برای پر کردن لیست در داده‌های زمان‌بندی راه‌اندازی لازم است نیز می‌شوند. این کار رفتار برنامه را برای کاربر تغییر نمی‌دهد، اما به داده‌های زمان‌بندی راه‌اندازی اجازه می‌دهد تا زمان لازم برای پر کردن لیست را نیز شامل شوند. reportFullyDrawn() تا زمانی که همه وظایف، صرف نظر از ترتیب آنها، تکمیل نشده باشند، فراخوانی نمی‌شود.

مثال زیر نشان می‌دهد که چگونه می‌توانید چندین وظیفه پس‌زمینه را همزمان اجرا کنید، و هر کدام گزارشگر خود را ثبت کنند:

کاتلین

class MainActivity : ComponentActivity() {

    sealed interface ActivityState {
        data object LOADING : ActivityState
        data object LOADED : ActivityState
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            var activityState by remember {
                mutableStateOf(ActivityState.LOADING as ActivityState)
            }
            fullyDrawnReporter.addOnReportDrawnListener {
                activityState = ActivityState.LOADED
            }
            ReportFullyDrawnTheme {
                when(activityState) {
                    is ActivityState.LOADING -> {
                        // Display the loading UI.
                    }
                    is ActivityState.LOADED -> {
                        // Display the full UI.
                    }
                }
            }
            SideEffect {
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
                fullyDrawnReporter.addReporter()
                lifecycleScope.launch(Dispatchers.IO) {
                    // Perform the background operation.
                    fullyDrawnReporter.removeReporter()
                }
            }
        }
    }
}

جاوا

public class MainActivity extends ComponentActivity {
    private FullyDrawnReporter fullyDrawnReporter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        fullyDrawnReporter = getFullyDrawnReporter();
        fullyDrawnReporter.addOnReportDrawnListener(() -> {
            // Trigger the UI update.
            return Unit.INSTANCE;
        });

        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                fullyDrawnReporter.addReporter();
                // Do the background work.
                fullyDrawnReporter.removeReporter();
            }
        }).start();
    }
}

اگر برنامه شما از Jetpack Compose استفاده می‌کند، می‌توانید از APIهای زیر برای نشان دادن حالت کاملاً ترسیم‌شده استفاده کنید:

  • ReportDrawn : نشان می‌دهد که ترکیب‌بندی شما بلافاصله برای تعامل آماده است.
  • ReportDrawnWhen : یک گزاره مانند list.count > 0 می‌گیرد تا نشان دهد چه زمانی composable شما برای تعامل آماده است.
  • ReportDrawnAfter : یک متد تعلیقی می‌گیرد که پس از تکمیل، نشان می‌دهد که composable شما برای تعامل آماده است.
شناسایی گلوگاه‌ها

برای یافتن گلوگاه‌ها، می‌توانید از Android Studio CPU Profiler استفاده کنید. برای اطلاعات بیشتر، به «بازرسی فعالیت CPU با CPU Profiler» مراجعه کنید.

همچنین می‌توانید از طریق ردیابی درون‌خطی (inline tracing) در داخل متدهای onCreate() برنامه‌ها و فعالیت‌های خود، بینشی نسبت به تنگناهای احتمالی به دست آورید. برای کسب اطلاعات در مورد ردیابی درون‌خطی، به مستندات توابع Trace و مرور کلی ردیابی سیستم مراجعه کنید.

حل مسائل رایج

این بخش به بررسی چندین مسئله می‌پردازد که اغلب بر عملکرد راه‌اندازی برنامه تأثیر می‌گذارند. این مسائل عمدتاً مربوط به مقداردهی اولیه اشیاء برنامه و فعالیت و همچنین بارگذاری صفحات هستند.

مقداردهی اولیه سنگین برنامه

وقتی کد شما شیء Application را override می‌کند و هنگام مقداردهی اولیه آن شیء، کارهای سنگین یا منطق پیچیده‌ای را اجرا می‌کند، عملکرد راه‌اندازی می‌تواند دچار مشکل شود. اگر زیرکلاس‌های Application شما مقداردهی‌های اولیه‌ای را انجام دهند که هنوز نیازی به انجام آنها نیست، ممکن است برنامه شما در هنگام راه‌اندازی زمان زیادی را هدر دهد.

برخی از مقداردهی‌های اولیه ممکن است کاملاً غیرضروری باشند، مانند زمان مقداردهی اولیه اطلاعات وضعیت برای فعالیت اصلی، زمانی که برنامه در واقع در پاسخ به یک intent راه‌اندازی می‌شود. با یک intent، برنامه فقط از زیرمجموعه‌ای از داده‌های وضعیت قبلاً مقداردهی شده استفاده می‌کند.

چالش‌های دیگر در طول راه‌اندازی برنامه شامل رویدادهای جمع‌آوری زباله (garbage collection) است که تأثیرگذار یا متعدد هستند، یا I/O دیسک که همزمان با راه‌اندازی اتفاق می‌افتد، که فرآیند راه‌اندازی را بیشتر مسدود می‌کند. جمع‌آوری زباله به ویژه در زمان اجرای Dalvik مورد توجه است؛ زمان اجرای اندروید (ART) جمع‌آوری زباله را به طور همزمان انجام می‌دهد و تأثیر آن عملیات را به حداقل می‌رساند.

تشخیص مشکل

می‌توانید از ردیابی متد یا ردیابی درون‌خطی برای تشخیص مشکل استفاده کنید.

ردیابی روش

اجرای CPU Profiler نشان می‌دهد که متد callApplicationOnCreate() در نهایت متد com.example.customApplication.onCreate شما را فراخوانی می‌کند. اگر این ابزار نشان دهد که اجرای این متدها مدت زمان زیادی طول می‌کشد، بیشتر بررسی کنید تا ببینید چه کاری در آنجا انجام می‌شود.

ردیابی درون خطی

از ردیابی درون‌خطی برای بررسی عوامل احتمالی، از جمله موارد زیر، استفاده کنید:

  • تابع اولیه‌ی onCreate() در برنامه‌ی شما.
  • هر شیء سینگلتون سراسری که برنامه شما مقداردهی اولیه می‌کند.
  • هرگونه ورودی/خروجی دیسک، deserialization یا حلقه‌های تنگ که ممکن است در طول گلوگاه رخ دهد.

راه حل های مشکل

چه مشکل مربوط به مقداردهی اولیه غیرضروری باشد و چه مربوط به ورودی/خروجی دیسک، راه حل مقداردهی اولیه کند (lazy initialization) است. به عبارت دیگر، فقط اشیاء مورد نیاز فوری را مقداردهی اولیه کنید. به جای ایجاد اشیاء استاتیک سراسری، به الگوی singleton بروید که در آن برنامه فقط در اولین باری که به اشیاء نیاز دارد، آنها را مقداردهی اولیه می‌کند.

همچنین، استفاده از یک چارچوب تزریق وابستگی مانند Hilt را در نظر بگیرید که اشیاء و وابستگی‌ها را هنگام تزریق برای اولین بار ایجاد می‌کند.

اگر برنامه شما از ارائه دهندگان محتوا برای مقداردهی اولیه اجزای برنامه در هنگام راه‌اندازی استفاده می‌کند، به جای آن از کتابخانه App Startup استفاده کنید.

مقداردهی اولیه فعالیت سنگین

ایجاد فعالیت اغلب مستلزم کار سربار زیادی است. اغلب، فرصت‌هایی برای بهینه‌سازی این کار برای دستیابی به بهبود عملکرد وجود دارد. چنین مسائل رایجی شامل موارد زیر است:

  • بزرگ کردن طرح‌بندی‌های بزرگ یا پیچیده.
  • مسدود کردن ترسیم صفحه روی دیسک یا ورودی/خروجی شبکه.
  • بارگذاری و رمزگشایی بیت‌مپ‌ها
  • تبدیل اشیاء VectorDrawable به تصویر شطرنجی.
  • مقداردهی اولیه سایر زیرسیستم‌های فعالیت.

تشخیص مشکل

در این مورد نیز، هم ردیابی متد و هم ردیابی درون‌خطی می‌توانند مفید باشند.

ردیابی روش

هنگام استفاده از CPU Profiler، به سازنده‌های زیرکلاس Application و متدهای com.example.customApplication.onCreate() در برنامه خود توجه کنید.

اگر ابزار نشان دهد که اجرای این متدها مدت زیادی طول می‌کشد، بیشتر بررسی کنید تا ببینید چه کاری در آنجا انجام می‌شود.

ردیابی درون خطی

از ردیابی درون‌خطی برای بررسی عوامل احتمالی، از جمله موارد زیر، استفاده کنید:

  • تابع اولیه‌ی onCreate() در برنامه‌ی شما.
  • هر شیء سینگلتون سراسری که مقداردهی اولیه شود.
  • هرگونه ورودی/خروجی دیسک، deserialization یا حلقه‌های تنگ که ممکن است در طول گلوگاه رخ دهد.

راه حل های مشکل

گلوگاه‌های بالقوه زیادی وجود دارد، اما دو مشکل رایج و راه‌حل‌های آنها به شرح زیر است:

  • هرچه سلسله مراتب نمای شما بزرگتر باشد، برنامه زمان بیشتری برای باد کردن آن صرف می‌کند. دو مرحله‌ای که می‌توانید برای حل این مشکل انجام دهید عبارتند از:
    • با کاهش طرح‌بندی‌های تکراری یا تو در تو، سلسله مراتب نمای خود را مسطح کنید.
    • بخش‌هایی از رابط کاربری که نیازی به نمایش آنها در هنگام اجرا نیست را بزرگ نکنید. در عوض، از یک شیء ViewStub به عنوان نگهدارنده‌ی زیرسلسله مراتب استفاده کنید تا برنامه بتواند در زمان مناسب‌تری آنها را بزرگ کند.
  • قرار دادن تمام مقداردهی اولیه منابع در نخ اصلی می‌تواند باعث کندی راه‌اندازی نیز شود. می‌توانید این مشکل را به صورت زیر حل کنید:
    • تمام مقداردهی اولیه منابع را جابجا کنید تا برنامه بتواند آن را به صورت تنبل (lazy) روی یک نخ متفاوت انجام دهد.
    • اجازه دهید برنامه نماهای شما را بارگذاری و نمایش دهد، و سپس بعداً ویژگی‌های بصری وابسته به بیت‌مپ‌ها و سایر منابع را به‌روزرسانی کنید.

صفحه‌های نمایش چلپ چلوپ سفارشی

اگر قبلاً از یکی از روش‌های زیر برای پیاده‌سازی صفحه شروع سفارشی در اندروید ۱۱ (سطح API 30) یا قبل از آن استفاده کرده باشید، ممکن است در هنگام راه‌اندازی زمان اضافی اضافه شود:

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

با شروع اندروید ۱۲، مهاجرت به API SplashScreen الزامی است. این API زمان راه‌اندازی سریع‌تری را فراهم می‌کند و به شما امکان می‌دهد صفحه شروع خود را به روش‌های زیر تنظیم کنید:

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

برای جزئیات بیشتر به راهنمای مهاجرت به صفحه Splash مراجعه کنید.

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}