Warm Start είναι ένα σενάριο εκκίνησης εφαρμογής Android όπου η διεργασία της εφαρμογής υπάρχει ήδη στη μνήμη (π.χ. μετά από ελαχιστοποίηση), αλλά το Activity έχει καταστραφεί από το σύστημα για εξοικονόμηση πόρων. Το Application.onCreate έχει ήδη εκτελεστεί, οι κλάσεις έχουν φορτωθεί, αλλά το UI δημιουργείται εκ νέου. Σύμφωνα με Google, 2024, το Warm Start διαρκεί 200 έως 800 ms και αποτελεί περίπου το 40% όλων των εκκινήσεων σε συσκευές με 4 GB RAM.
Κύρια σημεία
Warm Start (θερμή εκκίνηση) είναι μια κατάσταση μεταξύ Cold Start και Hot Start: η διεργασία της εφαρμογής υπάρχει στη μνήμη (μερικές φορές στην προσωρινή μνήμη παρασκηνίου Linux), αλλά το Activity δεν είναι ενεργό και θα δημιουργηθεί εκ νέου. Το σύστημα Android όταν υπάρχει έλλειψη RAM μπορεί να εκφορτώσει το Activity από τη στοίβα, αφήνοντας τη διεργασία ζωντανή. Όταν ο χρήστης επιστρέφει στην εφαρμογή, ξεκινά το Warm Start: δημιουργείται ένα νέο στιγμιότυπο Activity, εκτελούνται οι μέθοδοι κύκλου ζωής onCreate → onStart → onResume, αλλά το Application.onCreate και η φόρτωση κλάσεων παραλείπονται.
Το σύστημα Android αποφασίζει για την εκφόρτωση Activity με βάση την προτεραιότητα διεργασίας (importance rank). Ένα Activity στο παρασκήνιο (επίπεδο PROCESS_STATE_IMPORTANT_FOREGROUND ή PROCESS_STATE_TOP_SLEEPING) μπορεί να καταστραφεί 5–30 λεπτά μετά την ελαχιστοποίηση της εφαρμογής, ανάλογα με τη διαθέσιμη RAM. Σε συσκευές με 3 GB RAM, το Activity μπορεί να εκφορτωθεί μετά από 10 λεπτά, σε συσκευές με 8 GB — μετά από ώρες. Σημαντικό: σε Warm Start το onSaveInstanceState καλείται πριν από την καταστροφή του Activity και ο προγραμματιστής μπορεί να αποθηκεύσει την κατάσταση UI.
Ο χρήστης δεν βλέπει διαφορά μεταξύ Warm και Cold Start — απλά πατάει το εικονίδιο της εφαρμογής και περιμένει. Ωστόσο, σε Warm Start μπορεί να εμφανιστεί λευκή οθόνη (blank window) αν η εφαρμογή δεν έχει ρυθμίσει δικό της θέμα για το παράθυρο εκκίνησης. Η Google συνιστά να ορίσετε ένα προσαρμοσμένο θέμα στο manifest (Theme.AppCompat.Light ή Theme.Material3.DayNight) για το Activity εκκίνησης για να αποφύγετε το τρεμόπαιγμα λευκής/μαύρης οθόνης σε Warm Start. Σε Android 12+, το SplashScreen API κρύβει επίσης αυτό το εφέ.
Η κατανόηση της διαφοράς μεταξύ των τριών τύπων εκκίνησης είναι απαραίτητη για την επιλογή της σωστής στρατηγικής προφίλ και βελτιστοποίησης. Κάθε τύπος έχει τη δική του διάρκεια, τα δικά του σημεία συμφόρησης και τα δικά του εργαλεία μέτρησης.
| Κριτήριο | Cold Start | Warm Start | Hot Start |
|---|---|---|---|
| Διεργασία | Δημιουργείται εκ νέου | Υπάρχει στη μνήμη | Υπάρχει στη μνήμη |
| Application.onCreate | Εκτελείται | Δεν εκτελείται | Δεν εκτελείται |
| Activity | Δημιουργείται από την αρχή | Δημιουργείται από την αρχή | Επαναφέρεται από τη στοίβα |
| Χρόνος | 1–5 δευτερόλεπτα | 200–800 ms | < 200 ms |
| onCreate Activity | Πλήρης | Πλήρης (με restore) | Παραλείπεται |
Στην πράξη, το Warm Start αποτελεί το 30% έως 60% όλων των εκκινήσεων εφαρμογής, ανάλογα με τις συνήθειες του χρήστη και την ποσότητα RAM της συσκευής. Οι χρήστες που διατηρούν πολλές εφαρμογές ανοιχτές (multitasker) αντιμετωπίζουν συχνότερα Warm Start. Για κοινωνικά δίκτυα και messengers, το Warm Start είναι το πιο συνηθισμένο σενάριο επειδή η εφαρμογή είναι πάντα στο παρασκήνιο. Για τραπεζικές εφαρμογές, αντίθετα, κυριαρχεί το Cold Start (υποχρεωτικός καθαρισμός διεργασίας για λόγους ασφαλείας).
Το Warm Start αποτελείται από τρεις φάσεις, καθεμία από τις οποίες μπορεί να μετρηθεί και να βελτιστοποιηθεί. Σε αντίθεση με το Cold Start, εδώ δεν υπάρχει φάση fork και φόρτωσης κλάσεων, αλλά υπάρχει φάση επαναφοράς κατάστασης (restore) που μπορεί να είναι δαπανηρή.
Το σύστημα ελέγχει αν η εφαρμογή έχει θέμα για το παράθυρο εκκίνησης. Αν δεν έχει οριστεί θέμα, εμφανίζεται λευκή (ή μαύρη, ανάλογα με το σύστημα) οθόνη. Αν έχει οριστεί θέμα, εμφανίζεται το φόντο από το θέμα. Αυτή η φάση διαρκεί 10–30 ms, αλλά είναι οπτικά αισθητή αν το θέμα δεν ταιριάζει με το πραγματικό UI της εφαρμογής. Χρησιμοποιήστε Theme.Material3.DayNight με προσαρμοσμένο windowBackground του οποίου το χρώμα ταιριάζει με το φόντο της πρώτης οθόνης — αυτό δημιουργεί εφέ άμεσης φόρτωσης.
Το σύστημα καλεί onCreate με τη μετάδοση του Bundle savedInstanceState που αποθηκεύτηκε στο onSaveInstanceState πριν από την καταστροφή του Activity. Αν η εφαρμογή αποθήκευσε σωστά την κατάσταση (κείμενο πεδίων, θέση κύλισης, δεδομένα ViewModel), η επαναφορά γίνεται γρήγορα. Αν όχι — το Activity ξεκινά από λευκή σελίδα και ο χρήστης βλέπει loader μέχρι να φορτωθούν τα δεδομένα. Βασικό σημείο: τα αντικείμενα ViewModel επιβιώνουν από Warm Start μόνο αν η διεργασία δεν καταστράφηκε — σε Warm Start το ViewModel παραμένει στη μνήμη.
Μετά το onCreate, εκτελούνται onStart → onResume και το σύστημα καλεί την πρώτη απόδοση. Το TTFD (Time To First Draw) για Warm Start πρέπει να είναι λιγότερο από 300 ms σε μεσαία συσκευή. Αν η πρώτη οθόνη περιέχει σύνθετο RecyclerView με βαριά View ή φορτώνει εικόνες μέσω δικτύου, το 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. Για αναπαραγωγιμότητα χρησιμοποιήστε script: εκκίνηση → αναμονή → 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 ms — το τεστ αποτυγχάνει. Αυτό επιτρέπει την παρακολούθηση παλινδρομήσεων σε κάθε commit.
Το Firebase διακρίνει αυτόματα Cold και Warm Start με βάση τον χρόνο από το προηγούμενο κλείσιμο της εφαρμογής. Αν η εφαρμογή ήταν ανοιχτή τα τελευταία 30 λεπτά, το Firebase ταξινομεί την εκκίνηση ως Warm. Στην κονσόλο Firebase θα δείτε ξεχωριστά γραφήματα για κάθε τύπο εκκίνησης, επιτρέποντας την αξιολόγηση της αποτελεσματικότητας των βελτιστοποιήσεων. Για παράδειγμα, μετά την εφαρμογή αποθήκευσης κατάστασης στο ViewModel, μπορείτε να δείτε μείωση Warm Start έως 30%.
Η βελτιστοποίηση Warm Start επικεντρώνεται σε δύο κατευθύνσεις: επιτάχυνση του Activity.onCreate και σωστή επαναφορά κατάστασης. Επειδή το Application.onCreate και η φόρτωση κλάσεων έχουν ήδη γίνει, το κύριο σημείο συμφόρησης είναι ο κώδικας UI της πρώτης οθόνης.
Αν η αποθηκευμένη κατάσταση (savedInstanceState) περιέχει δεδομένα που πρέπει να αποσειριοποιηθούν (Bitmap, String, JSON), κάντε το σε νήμα παρασκηνίου. Αντί για άμεση ανάγνωση από Bundle στο onCreate, εκκινήστε ένα coroutine και εμφανίστε οθόνη shimmer. Στην πράξη, η αποσειριοποίηση Bundle σε μεσαία συσκευή διαρκεί 20–100 ms — φαίνεται λίγο, αλλά για Warm Start είναι 10–50% του συνολικού χρόνου. Χρησιμοποιήστε το Saved State Module της βιβλιοθήκης Jetpack που αποθηκεύει και επαναφέρει αυτόματα την κατάσταση ViewModel σε Bundle ή βάση δεδομένων.
Η ανάπτυξη XML διάταξης (layout inflation) — ένα από τα πιο δαπανηρά στάδια του Warm Start. Αν η πρώτη οθόνη χρησιμοποιεί σύνθετο CoordinatorLayout με AppBar, CollapsingToolbar, NestedScrollView και τρία RecyclerView, ο χρόνος inflation μπορεί να φτάσει τα 300 ms. Λύσεις: χρησιμοποιήστε ConstraintLayout για επίπεδη ιεραρχία, εφαρμόστε ViewStub για τμήματα αόρατα κατά την εκκίνηση (bottom sheet, dialog), ενεργοποιήστε ασύγχρονη ανάπτυξη για βαριά fragment μέσω AsyncLayoutInflater. Στο Jetpack Compose δεν απαιτείται inflation, αλλά η μεταγλώττιση του δέντρου Compose σε Warm Start μπορεί να διαρκέσει παρόμοιο χρόνο.
Σε Warm Start, τα δεδομένα που φόρτωσε η εφαρμογή στην προηγούμενη συνεδρία μπορεί να είναι ήδη στην κρυφή μνήμη: βάση δεδομένων Room, SharedPreferences, κρυφή μνήμη in-memory στο ViewModel. Αν η πρώτη οθόνη σας εμφανίζει λίστα από τον διακομιστή, ελέγξτε την κρυφή μνήμη κατά την εκκίνηση και ενημερώστε τα δεδομένα στο παρασκήνιο. Χρησιμοποιήστε τη στρατηγική cache-then-network: πρώτα εμφανίστε τα προσωρινά αποθηκευμένα δεδομένα (άμεσα), στη συνέχεια ενημερώστε από τον διακομιστή (ασύγχρονα). Αυτό μειώνει τον αντιληπτό χρόνο Warm Start σε 100–200 ms.
// ViewModel με προσωρινή αποθήκευση για Warm Start
class FeedViewModel : ViewModel() {
private val cache = MutableStateFlow<List<Item>>(emptyList())
init {
// Πρώτα κρυφή μνήμη, μετά δίκτυο
viewModelScope.launch {
cache.emit(db.getItems()) // Warm Start: δεδομένα ήδη στη βάση
cache.emit(api.fetchItems()) // Ενημέρωση στο παρασκήνιο
}
}
}
Η σωστή αποθήκευση κατάστασης είναι ο βασικός παράγοντας που διακρίνει ένα καλό Warm Start από ένα κακό. Ο χρήστης περιμένει να επιστρέψει στην εφαρμογή και να δει το ίδιο πράγμα που άφησε — συμπεριλαμβανομένης της θέσης κύλισης, κειμένου σε πεδία, επιλεγμένων καρτελών.
Το σύστημα καλεί onSaveInstanceState κατά την καταστροφή του Activity, αλλά ΠΡΙΝ η διεργασία μπορεί να σκοτωθεί. Στο Bundle αποθηκεύονται μόνο απλά δεδομένα (String, Int, Parcelable, Serializable). Για σύνθετα δεδομένα χρησιμοποιήστε SavedStateHandle στο ViewModel — αποθηκεύει και επαναφέρει αυτόματα πεδία σε Warm Start. Σε αντίθεση με το onSaveInstanceState, το SavedStateHandle λειτουργεί ακόμα κι αν η διεργασία επέζησε του Warm Start (το ViewModel δεν καταστρέφεται). Παράδειγμα: για κείμενο σε EditText χρησιμοποιήστε SavedStateHandle.getLiveData(“text”) — το κείμενο θα αποθηκευτεί και θα επαναφερθεί αυτόματα.
Αν σε Warm Start η διεργασία δεν σκοτώθηκε, το ViewModel παραμένει στη μνήμη και το onCleared δεν καλείται. Αυτό σημαίνει ότι όλα τα δεδομένα που φορτώθηκαν στην προηγούμενη συνεδρία είναι άμεσα διαθέσιμα. Αλλά αν η διεργασία σκοτώθηκε (συσκευή σε deep sleep για περισσότερο από 30 λεπτά), το ViewModel καταστρέφεται και δημιουργείται ξανά με SavedStateHandle. Για σωστή λειτουργία ViewModel σε Warm Start χρησιμοποιήστε SavedStateHandle με πεδία που πρέπει να επαναφέρονται σε οποιοδήποτε σενάριο. Διαφορά: το ViewModel με @HiltViewModel υποστηρίζει αυτόματα SavedStateHandle.
| Μηχανισμός | Διεργασία ζωντανή | Διεργασία σκοτωμένη |
|---|---|---|
| ViewModel | Δεδομένα στη μνήμη | Καταστράφηκε, δημιουργείται ξανά |
| SavedStateHandle | Δεδομένα στη μνήμη | Επαναφέρεται από Bundle |
| onSaveInstanceState | Καλείται κατά εκφόρτωση Activity | Δεν καλείται |
| Room DB | Κρυφή μνήμη διαθέσιμη | Κρυφή μνήμη διαθέσιμη (δίσκος) |
Ένα από τα πιο συνηθισμένα προβλήματα Warm Start — απώλεια θέσης κύλισης. Ο χρήστης κύλησε τη ροή στο 50ο στοιχείο, ελαχιστοποίησε την εφαρμογή, επέστρεψε — και βλέπει την αρχή της λίστας. Λύση: αποθηκεύστε το layoutManager.onSaveInstanceState (αποθηκεύει θέση και offset του πρώτου ορατού στοιχείου) και επαναφέρετέ το στο 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 χωρίς loader
// Μετά τη φόρτωση: το profile ενημερώνεται στο SavedStateHandle
Αν η πρώτη οθόνη περιέχει σύνθετη διάταξη (χάρτης, gradient, πολλές λίστες), χρησιμοποιήστε 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 GB RAM όταν πολλές εφαρμογές λειτουργούν ταυτόχρονα. Στην πράξη, το Warm Start είναι εγγυημένο μόνο για 10–20 λεπτά μετά την ελαχιστοποίηση σε συσκευές μεσαίας κατηγορίας.
Ναι, αν η διεργασία δεν σκοτώθηκε, το ViewModel παραμένει στη μνήμη και το onCleared δεν καλείται. Αυτό είναι το βασικό πλεονέκτημα του Warm Start: όλα τα φορτωμένα δεδομένα, αιτήματα δικτύου, κρυφή μνήμη στο ViewModel — άμεσα διαθέσιμα. Αν η διεργασία σκοτώθηκε, το ViewModel δημιουργείται ξανά μέσω ViewModelProvider.Factory ή @HiltViewModel και το SavedStateHandle επαναφέρει τα αποθηκευμένα πεδία.
Θεωρητικά, το Warm Start είναι πάντα πιο γρήγορο από το Cold Start, αλλά στην πράξη υπάρχουν σενάρια όπου η διαφορά είναι ελάχιστη: αν το Application.onCreate είναι ελαφρύ (50 ms) και το Activity.onCreate είναι βαρύ (800 ms), τότε το Warm Start (800 ms) είναι σχεδόν ίσο με το Cold Start (850 ms). Σε αυτή την περίπτωση πρέπει να βελτιστοποιήσετε όχι το Application, αλλά το Activity.onCreate — αυτό γίνεται το σημείο συμφόρησης για Warm Start.
Το SplashScreen API σε Android 12+ εμφανίζει ένα σύστημα splash (εικονίδιο σε χρωματιστό φόντο) αμέσως κατά την εκκίνηση — τόσο για Cold όσο και για Warm Start. Για Warm Start, το splash εμφανίζεται μόνο 100–300 ms, μετά το οποίο αντικαθίσταται από το UI της εφαρμογής. Το SplashScreen από μόνο του δεν επιταχύνει την εκκίνηση, αλλά καλύπτει τον χρόνο δημιουργίας Activity, βελτιώνοντας την αντίληψη.
Ναι, επειδή το Warm Start συμβαίνει 2–3 φορές συχνότερα από το Cold Start. Αν το Cold Start διαρκεί 1,2 δευτερόλεπτα και το Warm Start 600 ms, τότε το 40% των εκκινήσεων (Warm) διαρκεί ακόμα 0,6 δευτερόλεπτα, που είναι αισθητό. Η βελτιστοποίηση Warm Start σε 200–300 ms δίνει στον χρήστη την αίσθηση άμεσης επιστροφής. Σε συσκευές με 6+ GB RAM, το Warm Start μπορεί να αποτελεί έως και 80% όλων των εκκινήσεων και η βελτιστοποίησή του γίνεται προτεραιότητα.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης