Warm Start هو سيناريو إطلاق تطبيق Android حيث تكون عملية التطبيق موجودة بالفعل في الذاكرة (على سبيل المثال، بعد التصغير)، ولكن تم تدمير Activity بواسطة النظام لتوفير الموارد. تم تنفيذ Application.onCreate بالفعل، وتم تحميل الفئات، ولكن يتم إنشاء UI من جديد. وفقاً لـ Google، 2024، يستغرق Warm Start من 200 إلى 800 مللي ثانية ويمثل حوالي 40% من جميع عمليات الإطلاق على الأجهزة ذات 4 جيجابايت RAM.
الخلاصة
Warm Start هو حالة بين Cold Start وHot Start: عملية التطبيق موجودة في الذاكرة (أحياناً في ذاكرة التخزين المؤقت الخلفية لنظام Linux)، ولكن Activity غير نشطة وسيتم إنشاؤها من جديد. عندما تنفد RAM من Android، قد يزيل Activity من المكدس، تاركاً العملية حية. عندما يعود المستخدم إلى التطبيق، يحدث Warm Start: يتم إنشاء مثيل جديد لـ Activity، يتم تنفيذ طرق دورة الحياة onCreate ← onStart ← onResume، ولكن يتم تخطي Application.onCreate وتحميل الفئات.
يقرر Android إزالة Activity بناءً على أولوية العملية (مرتبة الأهمية). Activity في الخلفية (المستوى PROCESS_STATE_IMPORTANT_FOREGROUND أو PROCESS_STATE_TOP_SLEEPING) قد يتم تدميرها بعد 5–30 دقيقة من تصغير التطبيق، حسب RAM المتاحة. على الأجهزة ذات 3 جيجابايت RAM، قد يتم إزالة Activity خلال 10 دقائق؛ على الأجهزة ذات 8 جيجابايت RAM، بعد عدة ساعات. من المهم: أثناء Warm Start، يتم استدعاء onSaveInstanceState قبل تدمير Activity، ويمكن للمطور حفظ حالة UI.
لا يرى المستخدم الفرق بين Warm وCold Start — هو ببساطة يضغط على أيقونة التطبيق وينتظر. ومع ذلك، أثناء Warm Start، قد تظهر شاشة بيضاء إذا لم يقم التطبيق بتعيين سمة نافذة بدء مخصصة. Google توصي بتعيين سمة مخصصة في البيان (Theme.AppCompat.Light أو Theme.Material3.DayNight) لنشاط البدء لتجنب وميض الشاشة البيضاء/السوداء أثناء Warm Start. على Android 12+، تخفي API SplashScreen هذا التأثير أيضاً.
فهم الفرق بين أنواع الإطلاق الثلاثة ضروري لاختيار استراتيجية التنميط والتحسين الصحيحة. لكل نوع مدته الخاصة، واختناقاته، وأدوات القياس الخاصة به.
| المعيار | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| العملية | تنشأ من الصفر | موجودة في الذاكرة | موجودة في الذاكرة |
| Application.onCreate | يتم تنفيذه | لا يتم تنفيذه | لا يتم تنفيذه |
| Activity | تنشأ من الصفر | تنشأ من الصفر | تستعاد من المكدس |
| الوقت | 1–5 ثوانٍ | 200–800 مللي ثانية | < 200 مللي ثانية |
| onCreate Activity | كامل | كامل (مع استعادة) | يتم تخطيه |
عملياً، يمثل Warm Start من 30% إلى 60% من جميع عمليات إطلاق التطبيقات، حسب عادات المستخدم وRAM الجهاز. المستخدمون الذين يبقون العديد من التطبيقات مفتوحة (متعددو المهام) يواجهون Warm Start أكثر. للشبكات الاجتماعية والمراسلات، Warm Start هو السيناريو الأكثر شيوعاً لأن التطبيق دائماً في الخلفية. للتطبيقات المصرفية، على العكس، يسود Cold Start (تنظيف إجباري للعملية لأسباب أمنية).
يتكون Warm Start من ثلاث مراحل، كل منها يمكن قياسها وتحسينها. على عكس Cold Start، لا توجد مرحلة fork ولا تحميل فئات، ولكن هناك مرحلة استعادة الحالة التي قد تكون مكلفة.
يتحقق النظام مما إذا كان للتطبيق سمة لنافذة البدء. إذا لم يتم تعيين السمة، تظهر شاشة بيضاء (أو سوداء، حسب النظام). إذا تم تعيين السمة، يتم عرض خلفية السمة. تستغرق هذه المرحلة 10–30 مللي ثانية، لكنها ملحوظة بصرياً إذا كانت السمة لا تتطابق مع UI الفعلي للتطبيق. استخدم Theme.Material3.DayNight مع windowBackground مخصص يتطابق لونه مع خلفية الشاشة الأولى — وهذا يخلق تأثير تحميل فوري.
يستدعي النظام onCreate مرراً Bundle savedInstanceState الذي تم حفظه في onSaveInstanceState قبل تدمير Activity. إذا حفظ التطبيق الحالة بشكل صحيح (نص الحقول، موضع التمرير، بيانات ViewModel)، تتم الاستعادة بسرعة. إذا لم يكن كذلك، تبدأ Activity من الصفر ويرى المستخدم مؤشر تحميل بينما يتم تحميل البيانات. النقطة الرئيسية: كائنات ViewModel تنجو من Warm Start فقط إذا لم يتم تدمير العملية — أثناء Warm Start، يبقى ViewModel في الذاكرة.
بعد onCreate، يتم تنفيذ onStart ← onResume، ويقوم النظام بتشغيل الرسم الأول. TTFD (الوقت حتى الرسم الأول) لـ Warm Start يجب أن يكون أقل من 300 مللي ثانية على جهاز متوسط. إذا كانت الشاشة الأولى تحتوي على RecyclerView معقد مع Views ثقيلة أو تحميل صور من الشبكة، قد يتجاوز TTFD العتبة. استخدم Placeholder وShimmer لتحميل سلس للمحتوى بعد الإطار الأول.
قياس Warm Start أكثر تعقيداً من Cold Start لأنك تحتاج لمحاكاة الحالة حيث العملية حية ولكن Activity مدمرة. أمر ADB القياسي مع العلم -S لا يعمل — فهو يقتل العملية. استخدم طرقاً مختلفة لـ Warm Start.
أولاً، قم بتشغيل التطبيق عبر adb shell monkey أو اضغط على الأيقونة، ثم صغره (adb shell input keyevent 3 keyevent HOME). انتظر 5–10 ثوانٍ ليتمكن النظام من إزالة Activity، ثم قم بتشغيل adb shell am start -W (بدون -S). سيعيد الأمر وقت إطلاق أقصر من Cold Start. للتكرار، استخدم سكريبت: تشغيل ← انتظار ← home ← انتظار ← تشغيل.
# محاكاة Warm Start عبر ADB
$ adb shell am start -W \
com.example.app/.MainActivity
# الإخراج (Warm Start):
# ThisTime: 412 ms
# TotalTime: 412 ms
# WaitTime: 423 ms
مكتبة androidx.benchmark.macro تدعم قياس Warm Start. في الاختبار، قم بتعيين startupMode = StartupMode.WARM — ستقوم المكتبة بتشغيل التطبيق، وتصغيره، والانتظار (تأخير قابل للتكوين)، ثم قياس إعادة التشغيل. يقوم Macrobenchmark بـ 10–20 تكراراً ويحسب النسب المئوية. في CI/CD، يمكنك تعيين عتبة: إذا تجاوز P50 Warm Start 600 مللي ثانية، يفشل الاختبار. هذا يسمح بتتبع الانحدارات مع كل commit.
يميز Firebase تلقائياً بين Cold وWarm Start بناءً على الوقت منذ آخر إغلاق للتطبيق. إذا تم فتح التطبيق خلال آخر 30 دقيقة، يصنف Firebase الإطلاق كـ Warm. في وحدة تحكم Firebase، سترى رسوماً بيانية منفصلة لكل نوع إطلاق، مما يسمح لك بتقييم فعالية التحسينات. على سبيل المثال، بعد تنفيذ الحفاظ على الحالة في ViewModel، قد ترى انخفاضاً بنسبة 30% في وقت Warm Start.
يركز تحسين Warm Start على مجالين: تسريع Activity.onCreate والاستعادة الصحيحة للحالة. نظراً لأن Application.onCreate وتحميل الفئات قد اكتملا بالفعل، فإن الاختناق الرئيسي هو كود UI للشاشة الأولى.
إذا كانت الحالة المحفوظة (savedInstanceState) تحتوي على بيانات تحتاج إلى إلغاء التسلسل (Bitmap, String, JSON)، قم بذلك في سلسلة خلفية. بدلاً من القراءة مباشرة من Bundle في onCreate، قم بتشغيل coroutine وأظهر شاشة shimmer. عملياً، إلغاء تسلسل Bundle على جهاز متوسط يستغرق 20–100 مللي ثانية — يبدو قليلاً، ولكن لـ Warm Start، هذا 10–50% من الوقت الإجمالي. استخدم Saved State Module من Jetpack، الذي يحفظ ويستعيد تلقائياً حالة ViewModel في Bundle أو قاعدة البيانات.
تضخيم تخطيط XML هو أحد أغلى مراحل Warm Start. إذا كانت الشاشة الأولى تستخدم CoordinatorLayout معقداً مع AppBar وCollapsingToolbar وNestedScrollView وثلاث RecyclerViews، يمكن أن يصل وقت التضخيم إلى 300 مللي ثانية. الحلول: استخدم ConstraintLayout لتسلسل هرمي مسطح، طبق ViewStub للأقسام غير المرئية عند البدء (bottom sheet, dialog)، فعّل التضخيم غير المتزامن للـ fragments الثقيلة عبر AsyncLayoutInflater. في Jetpack Compose، لا حاجة للتضخيم، لكن تجميع شجرة Compose أثناء Warm Start قد يستغرق وقتاً مماثلاً.
أثناء Warm Start، البيانات التي حملها التطبيق في الجلسة السابقة قد تكون بالفعل في الذاكرة المؤقتة: قاعدة بيانات Room، SharedPreferences، ذاكرة مؤقتة في ViewModel. إذا كانت شاشتك الأولى تعرض قائمة من الخادم، تحقق من الذاكرة المؤقتة عند البدء وحدّث البيانات في الخلفية. استخدم استراتيجية cache-then-network: اعرض أولاً البيانات المخزنة مؤقتاً (فورياً)، ثم حدّث من الخادم (بشكل غير متزامن). هذا يقلل وقت Warm Start المدرك إلى 100–200 مللي ثانية.
// ViewModel مع التخزين المؤقت لـ Warm Start
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// ذاكرة مؤقتة أولاً، ثم الشبكة
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start: البيانات موجودة بالفعل في BD
cache.emit(api.fetchItems()) // تحديث في الخلفية
}
}
}
الحفاظ الصحيح على الحالة هو العامل الرئيسي الذي يميز Warm Start الجيد عن السيئ. يتوقع المستخدم العودة إلى التطبيق ورؤية ما تركه تماماً — بما في ذلك موضع التمرير، النص في الحقول، وعلامات التبويب المحددة.
يستدعي النظام onSaveInstanceState عندما يتم تدمير Activity، ولكن قبل أن يتم قتل العملية. يتم حفظ أنواع البيانات البسيطة فقط (String, Int, Parcelable, Serializable) في Bundle. للبيانات المعقدة، استخدم SavedStateHandle في ViewModel — فهو يحفظ ويستعيد الحقول تلقائياً أثناء Warm Start. على عكس onSaveInstanceState، يعمل SavedStateHandle حتى إذا نجت العملية من Warm Start (ViewModel لا يُدمر). مثال: للنص في EditText، استخدم SavedStateHandle.getLiveData(“text”) — سيتم حفظ النص واستعادته تلقائياً.
إذا لم تُقتل العملية أثناء Warm Start، يبقى ViewModel في الذاكرة ولا يتم استدعاء onCleared. هذا يعني أن جميع البيانات المحملة في الجلسة السابقة متاحة فورياً. ومع ذلك، إذا قُتلت العملية (الجهاز في سكون عميق لأكثر من 30 دقيقة)، يتم تدمير ViewModel وإنشاؤه من جديد مع SavedStateHandle. لسلوك ViewModel الصحيح أثناء Warm Start، استخدم SavedStateHandle مع الحقول التي تحتاج إلى الاستعادة في أي سيناريو. الفرق: ViewModel مع @HiltViewModel يدعم SavedStateHandle تلقائياً.
| الآلية | العملية حية | العملية مقتولة |
|---|---|---|
| ViewModel | البيانات في الذاكرة | مدمر، يُنشأ من جديد |
| SavedStateHandle | البيانات في الذاكرة | يُستعاد من Bundle |
| onSaveInstanceState | يُستدعى عند إزالة Activity | لا يُستدعى |
| Room DB | الذاكرة المؤقتة متاحة | الذاكرة المؤقتة متاحة (قرص) |
أحد أكثر مشاكل Warm Start شيوعاً — فقدان موضع التمرير. قام المستخدم بالتمرير إلى العنصر 50، صغر التطبيق، عاد — ويرى بداية القائمة. الحل: احفظ layoutManager.onSaveInstanceState (يحفظ الموضع والإزاحة لأول عنصر مرئي) واستعدده في onRestoreInstanceState. يمكنك أيضاً حفظ آخر موضع مرئي في SharedPreferences بمفتاح تاريخ/وقت لاستعادة الموضع بسرعة أثناء Warm Start.
// حفظ موضع تمرير RecyclerView
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(
"rv_state", binding.recyclerView
.layoutManager?.onSaveInstanceState()
)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.getParcelable<Parcelable>("rv_state")
?.let { binding.recyclerView.layoutManager?.onRestoreInstanceState(it) }
}
مثالان عمليان لتحسين Warm Start: استخدام SavedStateHandle في ViewModel والاستعادة غير المتزامنة للبيانات المعقدة بعد الإطلاق.
SavedStateHandle يحفظ الحقول تلقائياً في Bundle ويستعيدها أثناء Warm Start. سيتم استعادة حقل ملف تعريف المستخدم (String, JSON) دون طلبات غير ضرورية للخادم. إذا قُتلت العملية، يقوم SavedStateHandle بتحميل آخر حالة محفوظة من Bundle.
class ProfileViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val profile: StateFlow<Profile?>
get() = savedStateHandle
.getStateFlow("profile", null)
fun loadProfile(id: String) {
viewModelScope.launch {
savedStateHandle["profile"] =
api.getProfile(id)
}
}
}
// Warm Start: profile ليس null، UI بدون محمل
// بعد التحميل: profile يتم تحديثه في SavedStateHandle
إذا كانت الشاشة الأولى تحتوي على تخطيط معقد (خريطة، تدرج، قوائم متعددة)، استخدم AsyncLayoutInflater لتضخيم العناصر الثقيلة في الخلفية. بينما يتم تضخيم التخطيط، اعرض placeholder مع تأثير shimmer. هذا مهم بشكل خاص لـ Warm Start، حيث كل ملي ثانية لها أهميتها. يعمل AsyncLayoutInflater على سلسلة خلفية ويمرر View الجاهز إلى callback في السلسلة الرئيسية.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// تخطيط placeholder للعرض الفوري
setContentView(R.layout.placeholder_shimmer)
// تحميل غير متزامن للتخطيط الثقيل
AsyncLayoutInflater(this).inflate(
R.layout.activity_main_complex,
findViewById(R.id.container)
) { view, resId, parent ->
parent?.removeAllViews()
parent?.addView(view)
}
}
}
الأسئلة الشائعة
نعم، إذا قرر النظام في لحظة Warm Start قتل عملية التطبيق (مثلاً لتحرير الذاكرة لتطبيق آخر)، يصبح الإطلاق Cold Start من الصفر. يحدث هذا على الأجهزة ذات 2–3 جيجابايت RAM عند تشغيل تطبيقات متعددة في وقت واحد. في الواقع، Warm Start مضمون فقط لمدة 10–20 دقيقة بعد التصغير على الأجهزة المتوسطة.
نعم، إذا لم تُقتل العملية، يبقى ViewModel في الذاكرة ولا يتم استدعاء onCleared. هذه ميزة رئيسية لـ Warm Start: جميع البيانات المحملة عبر طلبات الشبكة، الذاكرة المؤقتة في ViewModel — كلها متاحة فورياً. إذا قُتلت العملية، يتم إنشاء ViewModel من جديد عبر ViewModelProvider.Factory أو @HiltViewModel، ويستعيد SavedStateHandle الحقول المحفوظة.
نظرياً، Warm Start دائماً أسرع من Cold Start، لكن عملياً هناك سيناريوهات يكون فيها الفرق ضئيلاً: إذا كان Application.onCreate خفيفاً (50 مللي ثانية) وActivity.onCreate ثقيلاً (800 مللي ثانية)، فإن Warm Start (800 مللي ثانية) يكاد يساوي Cold Start (850 مللي ثانية). في هذه الحالة، يجب تحسين ليس Application، بل Activity.onCreate — يصبح هو الاختناق لـ Warm Start.
تظهر API SplashScreen على Android 12+ شاشة بدء النظام (أيقونة على خلفية ملونة) فوراً عند الإطلاق — لكل من Cold وWarm Start. لـ Warm Start، تظهر الشاشة لمدة 100–300 مللي ثانية فقط، وبعدها يتم استبدالها بـ UI التطبيق. SplashScreen لا تسرع الإطلاق نفسه، لكنها تخفي وقت إنشاء Activity، محسنة الإدراك.
نعم، لأن Warm Start يحدث 2–3 مرات أكثر من Cold Start. إذا استغرق Cold Start 1.2 ثانية وWarm Start 600 مللي ثانية، فإن 40% من عمليات الإطلاق (Warm) لا تزال تستغرق 0.6 ثانية، وهو أمر ملحوظ. تحسين Warm Start إلى 200–300 مللي ثانية يعطي المستخدم شعوراً بالعودة الفورية. على الأجهزة ذات 6+ جيجابايت RAM، يمكن أن يشكل Warm Start ما يصل إلى 80% من جميع عمليات الإطلاق، مما يجعل تحسينه أولوية.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.
اقرأ أيضًا