زمان راه‌اندازی برنامه

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

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

آشنایی با وضعیت‌های مختلف راه‌اندازی برنامه

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

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

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

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

شروع سرد

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

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

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

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

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

  1. شیء برنامه را ایجاد کنید.
  2. رشته اصلی را راه‌اندازی کنید.
  3. فعالیت اصلی را ایجاد کنید.
  4. مقداردهی اولیه واسط کاربر.
  5. واسط کاربر را روی صفحه بکشید.

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

برای اطلاعات بیشتر درباره فازهای چیدمان در Compose، به فازهای Jetpack Compose مراجعه کنید.

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

ساختن برنامه

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

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

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

ایجاد فعالیت

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

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

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

شروع گرم

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

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

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

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

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

بااین‌حال، اگر در پاسخ به رویدادهای پیرایش حافظه، مانند onTrimMemory، مقداری حافظه پاک شود، این اشیاء باید در پاسخ به رویداد راه‌اندازی گرم دوباره ایجاد شوند.

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

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

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

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

  1. در Perfetto، ردیف دارای سنجه مشتق‌شده «راه‌اندازی برنامه Android» را پیدا کنید. اگر آن را نمی‌بینید، بااستفاده از برنامه ردیابی سیستم درون‌دستگاهی، ردیابی را ضبط کنید.

    شکل ۲. برش سنجه مشتق‌شده «راه‌اندازی برنامه Android» در Perfetto.
  2. روی برش مربوطه کلیک کنید و m را فشار دهید تا برش انتخاب شود. کروشه‌هایی در اطراف برش ظاهر می‌شود و نشان می‌دهد که چقدر طول کشیده است. مدت زمان در برگه انتخاب فعلی نیز نشان داده می‌شود.

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

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

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

    شکل ۳. برش سنجه مشتق‌شده «راه‌اندازی برنامه Android» درکنار رشته اصلی برنامه.
  6. بخش سنجه‌های مشتق‌شده دیدن آنچه دقیقاً در راه‌اندازی برنامه گنجانده شده است را آسان‌تر می‌کند، بنابراین می‌توانید با جزئیات بیشتری به اشکال‌زدایی ادامه دهید.

هنگام تجزیه‌وتحلیل برنامه Jetpack Compose، می‌توانید از ردگیری ترکیب‌بندی برای دیدن جزئیات دقیق درباره عملکرد واسط کاربر استفاده کنید. به بخش‌های ردیابی در رشته اصلی که با Choreographer#doFrame شروع می‌شوند توجه ویژه داشته باشید. به‌دنبال برش‌هایی مثل Compose:recompose، Compose:layout، و Compose:draw باشید. این سنجه‌ها نشان می‌دهند که برنامه‌تان چقدر زمان صرف ترکیب‌بندی، اندازه‌گیری و جای‌گذاری، و طراحی مؤلفه‌های خاص می‌کند.

استفاده از سنجه‌ها برای بررسی و بهبود راه‌اندازی‌ها

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

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

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

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

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

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

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

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

بازیابی TTID

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

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

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

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

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

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

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

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

توصیه می‌کنیم از Logcat در Android Studio استفاده کنید، اما اگر از Android Studio استفاده نمی‌کنید، می‌توانید TTID را با اجرای برنامه‌تان با adb فرمان مدیر فعالیت پوسته نیز اندازه‌گیری کنید. مثالی در اینجا ارائه شده است:

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 را هنگام رندر کردن قاب اولیه توسط پنجره میزبان تعیین کند، اما نمی‌تواند TTFD را به‌طور خودکار تعیین کند. چون برنامه‌ها اغلب محتوای اصلی‌شان را به‌صورت ناهمگام بار می‌کنند، سیستم نمی‌داند چه زمانی برنامه واقعاً برای کاربر کاملاً قابل‌استفاده است. برای تعیین TTFD، برنامه باید وقتی به حالت کاملاً ترسیم‌شده می‌رسد، به سیستم سیگنال دهد.

بازیابی TTFD

برای پیدا کردن TTFD، با فراخوانی روش reportFullyDrawn مربوط به ComponentActivity، وضعیت کاملاً ترسیم‌شده را علامت‌گذاری کنید. روش reportFullyDrawn زمانی گزارش می‌دهد که برنامه کاملاً ترسیم شده و در وضعیت قابل‌استفاده باشد. «زمان تا نمایش کامل» مدت‌زمان سپری‌شده از وقتی است که سیستم قصد راه‌اندازی برنامه را دریافت می‌کند تا وقتی که reportFullyDrawn فراخوانی می‌شود. اگر reportFullyDrawn را فراخوانی نکنید، هیچ مقدار TTFD گزارش نمی‌شود.

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

وقتی از reportFullyDrawn استفاده می‌کنید، Logcat بروندادی مانند مثال زیر را نمایش می‌دهد که در آن TTFD برابر با ۱ ثانیه و ۵۴ میلی‌ثانیه است:

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

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

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

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

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

اگر برنامه‌تان بار کردن کُند انجام می‌دهد و نمایش اولیه شامل همه منابع نمی‌شود، مثلاً وقتی برنامه‌تان درحال واکشی تصاویر از شبکه است، شاید بخواهید فراخوانی reportFullyDrawn را تا زمانی که برنامه‌تان قابل‌استفاده شود به‌تأخیر بیندازید تا بتوانید جمعیت فهرست را به‌عنوان بخشی از زمان‌بندی محک‌زنی‌تان اضافه کنید.

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

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

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

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

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

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

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()
                }
            }
        }
    }
}
شناسایی گلوگاه‌ها

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

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

حل کردن مشکلات رایج

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

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

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

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

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

تشخیص مشکل

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

ردیابی روش‌ها

اجرای «تحلیل‌گر واحد پردازش مرکزی» نشان می‌دهد که روش callApplicationOnCreate درنهایت روش com.example.customApplication.onCreate شما را فراخوانی می‌کند. اگر ابزار نشان می‌دهد که این روش‌ها زمان زیادی برای تکمیل اجرا نیاز دارند، بیشتر کاوش کنید تا ببینید چه کاری در آنجا درحال انجام است.

ردیابی به‌خط

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

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

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

چه مشکل مربوط به مقداردهی‌های اولیه غیرضروری باشد چه مربوط به ورودی/خروجی دیسک، راه‌حل مقداردهی اولیه تنبل است. به عبارت دیگر، فقط اشیایی را مقداردهی اولیه کنید که فوراً به آن‌ها نیاز است. به‌جای ایجاد اشیای ثابت سراسری، به الگوی تک‌قلو بروید که در آن برنامه اشیا را فقط اولین باری که به آن‌ها نیاز دارد مقداردهی اولیه می‌کند.

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

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

راه‌اندازی فعالیت سنگین

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

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

تشخیص مشکل

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

ردیابی روش‌ها

هنگام استفاده از «تحلیل‌گر واحد پردازش مرکزی»، به سازنده‌های زیرکلاس Application و روش‌های com.example.customApplication.onCreate برنامه‌تان توجه کنید.

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

ردیابی به‌خط

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

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

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

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

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

برای اطلاعات بیشتر درباره بازترکیب، بازترکیب و دریافت تعداد بازترکیب را ببینید.

صفحه اسپلش سفارشی

اگر قبلاً از یکی از روش‌های زیر برای پیاده‌سازی صفحه اسپلش سفارشی در Android نسخه ۱۱ (سطح API‏ ۳۰) یا قدیمی‌تر استفاده کرده باشید، ممکن است درطول راه‌اندازی زمان اضافه‌ای را مشاهده کنید:

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

از Android 12، انتقال به میانای برنامه‌سازی کاربردی SplashScreen الزامی است. این میانای برنامه‌سازی کاربردی زمان راه‌اندازی را سریع‌تر می‌کند و به شما امکان می‌دهد صفحه اسپلش را به روش‌های زیر تغییر دهید:

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

برای جزئیات، به راهنمای انتقال صفحه اسپلش مراجعه کنید.