onCreate — to pierwsza i jedyna obowiązkowa metoda cyklu życia Activity i Fragment w Android. System wywołuje ją raz przy tworzeniu komponentu, przekazując parametr Bundle z wcześniej zapisanym stanem. Wewnątrz onCreate programista inicjalizuje interfejs użytkownika, wiąże elementy View, konfiguruje procedury obsługi zdarzeń i przywraca dane z savedInstanceState. Bez poprawnej implementacji onCreate nie można uruchomić żadnej aplikacji Android — to punkt wejścia dla każdego ekranu. Więcej o ogólnym cyklu życia Activity przeczytasz w artykule Activity Lifecycle.
Najważniejsze
onCreate — metoda zwrotna (callback), którą Android wywołuje przy tworzeniu nowego egzemplarza Activity lub Fragment. To pierwszy punkt wejścia do kodu ekranu użytkownika: przed wywołaniem onCreate żaden kod użytkownika nie jest wykonywany. System przekazuje do metody parametr Bundle, który albo zawiera wcześniej zapisane dane (przy ponownym tworzeniu), albo jest równy null (przy pierwszym uruchomieniu).
Metoda onCreate jest zdefiniowana w klasie android.app.Activity i w klasie androidx.fragment.app.Fragment. Obie wersje wykonują podobne zadania: inicjalizacja komponentu, konfiguracja UI i przywracanie stanu. Jednak konkretna implementacja różni się — Activity używa setContentView do załadowania układu, a Fragment zwraca View przez onCreateView. Programista musi nadpisać przynajmniej onCreate w Activity — bez tego Android nie będzie mógł wyświetlić ekranu.
onCreate jest wywoływany ściśle raz na pełny cykl życia egzemplarza Activity. Nawet przy obrocie ekranu nowy egzemplarz Activity otrzymuje nowe wywołanie onCreate z Bundle od poprzedniego egzemplarza. Ta właściwość czyni onCreate idealnym miejscem do jednorazowej inicjalizacji: ładowanie danych, tworzenie adapterów, konfiguracja komponentów DI przez Dagger lub Hilt.
W Activity metoda onCreate wykonuje cztery kluczowe zadania: ładowanie układu layout, inicjalizacja elementów View, przywracanie stanu z Bundle i konfiguracja podstawowych procedur obsługi zdarzeń. Obowiązkowy minimalny kod w onCreate — wywołanie super.onCreate(savedInstanceState) i setContentView(R.layout.activity_main).
class MainActivity : AppCompatActivity() {
private var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ViewBinding — nowoczesny zamiennik findViewById
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding?.root)
// Inicjalizacja za pomocą binding
binding?.apply {
welcomeText.text = getString(R.string.welcome)
startButton.setOnClickListener { startGame() }
}
// Przywracanie stanu
if (savedInstanceState != null) {
score = savedInstanceState.getInt("score", 0)
binding?.scoreText?.text = score.toString()
}
}
}
Nowoczesna praktyka — użycie ViewBinding zamiast findViewById. ViewBinding generuje klasę ActivityMainBinding na etapie kompilacji, co eliminuje błędy z nieprawidłowymi ID i zmniejsza ilość kodu szablonowego. Google zaleca ViewBinding jako standardowy sposób dostępu do View w Activity i Fragment od Android Studio 3.6.
Kolejność działań w onCreate musi być ścisła: najpierw super, potem setContentView, następnie wszystko inne. Wywołanie findViewById przed setContentView zwróci null — układ nie został jeszcze załadowany, a elementy View nie istnieją w hierarchii. To jeden z najczęstszych błędów początkujących programistów Android.
onCreate we Fragment różni się od Activity: tutaj nie wywołuje się setContentView, a wykonuje się tylko inicjalizację danych niezwiązanych z UI. Fragment dzieli tworzenie komponentu i tworzenie View na dwie osobne metody: onCreate (wywoływana raz) i onCreateView (wywoływana za każdym razem przy tworzeniu lub ponownym tworzeniu View).
class UserListFragment : Fragment() {
private lateinit var viewModel: UserViewModel
private var binding: FragmentUserListBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Inicjalizacja ViewModel — przetrwa ponowne utworzenie View
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
// Argumenty z FragmentManager
arguments?.let {
viewModel.loadUser(it.getString("user_id") ?: "")
}
// Zachowanie przy obrocie
retainInstance = true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentUserListBinding.inflate(inflater, container, false)
return binding!!.root
}
}
Kluczowa różnica między onCreate Activity i Fragment: onCreate we Fragment nie powinien zawierać kodu związanego z View, ponieważ View może być zniszczony i utworzony ponownie (na przykład przy przełączaniu zakładek ViewPager), a onCreate jest wywoływany tylko raz. Ładowanie danych, konfiguracja ViewModel i inicjalizacja adapterów — zadania onCreate, a wiązanie View — zadanie onViewCreated.
Parametr savedInstanceState w onCreate — to mechanizm zapisywania i przywracania tymczasowego stanu Activity lub Fragment. Gdy system niszczy Activity (obrót ekranu, brak pamięci), wywołuje onSaveInstanceState(), do którego programista umieszcza parę klucz-wartość w Bundle. Przy tworzeniu nowego egzemplarza ten Bundle jest zwracany w onCreate.
Bundle obsługuje następujące typy danych: String, Integer, Boolean, Long, Float, Double, ich tablice, a także obiekty Parcelable i Serializable. Dla złożonych obiektów używa się Parcelable — to bardziej wydajny mechanizm serializacji, specyficzny dla Android. Rozmiar Bundle jest ograniczony do około 500 KB — przekroczenie limitu powoduje wyjątek TransactionTooLargeException.
companion object {
private const val KEY_USER_NAME = "user_name"
private const val KEY_SCORE = "score"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_game)
if (savedInstanceState != null) {
userName = savedInstanceState.getString(KEY_USER_NAME) ?: ""
currentScore = savedInstanceState.getInt(KEY_SCORE)
}
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString(KEY_USER_NAME, userName)
outState.putInt(KEY_SCORE, currentScore)
}
Ważne jest zrozumienie: onSaveInstanceState nie jest wywoływane, gdy użytkownik jawnie zamyka Activity przez finish() lub naciśnięcie przycisku „Wstecz"). System uważa, że w tym przypadku użytkownik świadomie kończy pracę i zapisywanie stanu nie jest wymagane. Dlatego poleganie wyłącznie na savedInstanceState do długoterminowego przechowywania danych jest niewłaściwe — używaj Room, DataStore lub SharedPreferences.
onCreate jest wykonywany w głównym (UI) wątku, a system czeka na jego zakończenie, zanim wyświetli Activity na ekranie. Jeśli onCreate wykonuje się dłużej niż 5 sekund, system pokazuje okno dialogowe ANR (Application Not Responding) i proponuje użytkownikowi zamknięcie aplikacji. Długotrwałe operacje, takie jak ładowanie danych z sieci lub odczyt z bazy danych, powinny być przeniesione do wątku tła.
Zgodnie z zaleceniami Google Android Performance (2025), onCreate powinien zakończyć się w mniej niż 1 sekundę na urządzeniach średniej klasy. W tym celu należy: używać leniwej inicjalizacji (lazy delegate w Kotlin), odłożyć ładowanie ciężkich danych do onResume lub przez korutyny, stosować ViewStub dla rzadko używanych komponentów UI, profilować czas uruchamiania przez Android Vitals.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Leniiwa inicjalizacja — obiekt tworzony tylko przy pierwszym dostępie
val heavyData by lazy {
HeavyDataLoader.load()
}
// Ładowanie danych w wątku tła przez lifecycleScope
lifecycleScope.launch(Dispatchers.IO) {
val users = userDao.getAllUsers()
withContext(Dispatchers.Main) {
adapter.submitList(users)
}
}
}
Narzędzia profilowania: Android Studio Profiler (zakładka CPU) pokazuje dokładny czas wykonania każdej metody. W Android Vitals (konsola Google Play) można śledzić metrykę „Czas zimnego startu") — jeśli onCreate twojego Activity przekracza 500 ms, konsola oznacza to jako problem wydajności. My w IT Sectr używamy testów Macrobenchmark do automatycznej kontroli czasu uruchamiania każdej Activity w CI-pipeline.
ViewModel — najlepszy sposób inicjalizacji danych w onCreate, które powinny przetrwać obrót ekranu. ViewModel jest tworzony w onCreate przez ViewModelProvider i automatycznie zachowywany przy zmianie konfiguracji. Gdy Activity jest ponownie tworzone po obrocie, ViewModel pozostaje w pamięci, a onCreate otrzymuje ten sam ViewModel bez utraty danych.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
// ViewModel tworzony raz i przetrwa zmiany konfiguracji
val viewModel: ProfileViewModel =
ViewModelProvider(this)[ProfileViewModel::class.java]
// Obserwacja LiveData — UI automatycznie aktualizuje się przy zmianie danych
viewModel.user.observe(this) { user ->
binding?.userName?.text = user.name
binding?.userEmail?.text = user.email
}
// Ładowanie danych, jeśli ViewModel właśnie został utworzony
if (savedInstanceState == null) {
viewModel.loadProfile(userId)
}
}
Kombinacja ViewModel + LiveData/StateFlow rozwiązuje problem obrotu ekranu bez ręcznego zapisywania w Bundle. ViewModel przechowuje dane w pamięci, LiveData automatycznie ponownie subskrybuje Activity przy ponownym tworzeniu, a StateFlow (z Kotlin Coroutines) dodaje reaktywność z obsługą korutyn. To standardowa architektura zalecana przez Google w przewodniku Guide to App Architecture.
Nawet doświadczeni programiści popełniają typowe błędy w onCreate. Omówmy pięć najczęstszych problemów i sposoby ich unikania.
Najczęstszy błąd — próba znalezienia View przez findViewById przed wywołaniem setContentView. Wszystkie elementy View są tworzone w momencie inflacji układu, więc każde odwołanie do findViewById przed setContentView zwraca null i powoduje NullPointerException przy próbie użycia View. Rozwiązanie: ścisła kolejność — najpierw super, potem setContentView, następnie findViewById lub ViewBinding.
Ładowanie danych z sieci, odczyt z bazy danych lub przetwarzanie dużych tablic bezpośrednio w onCreate blokuje renderowanie pierwszej klatki. Użytkownik widzi czarny ekran, dopóki onCreate się nie zakończy, co pogarsza postrzeganie szybkości działania aplikacji. Rozwiązanie: używać lifecycleScope.launch do operacji asynchronicznych, wyświetlać szkielet (placeholder UI) do czasu zakończenia ładowania.
Jeśli przy obrocie ekranu nie przywraca się stanu z Bundle, użytkownik traci cały niezapisany wkład: tekst w polach formularza, pozycję scrolla, wybrane elementy. Rozwiązanie: zawsze sprawdzać savedInstanceState != null w onCreate do przywracania danych, nawet jeśli wydaje się, że utrata stanu jest mało prawdopodobna.
Anonimowe klasy i lambdy w onCreate mogą niejawnie utrzymywać referencję do Activity po jego zniszczeniu. Na przykład Handler utworzony w onCreate kontynuuje wykonywanie odroczonych zadań nawet po zniszczeniu Activity. Rozwiązanie: używać LifecycleObserver, ViewModel i lifecycleScope, które automatycznie anulują zadania przy zniszczeniu.
Inicjalizacja View w onCreate Fragment — błąd logiczny, ponieważ View może być ponownie utworzone bez wywołania onCreate. Jeśli ustawi się nasłuchiwacza w onCreate, a View wiąże się w onCreateView, przy ponownym tworzeniu nasłuchiwacz pozostanie na starym View. Rozwiązanie: całą pracę z View wykonywać w onViewCreated, a onCreate pozostawić tylko do inicjalizacji warstwy danych.
Często zadawane pytania
Tak, nadpisywanie onCreate jest obowiązkowe dla każdej Activity, która wyświetla interfejs użytkownika. Bez tego nie można wywołać setContentView i załadować układu XML. Jeśli Activity nie ma UI (na przykład przezroczysta Activity-zastawka), onCreate i tak jest nadpisywane, ale bez wywołania setContentView.
Nie, onCreate nie może być wywołany ponownie dla tego samego egzemplarza Activity. Jeśli Activity jest zniszczona i utworzona na nowo (obrót ekranu, brak pamięci), to już nowy egzemplarz z nowym wywołaniem onCreate. Wyjątek — metoda recreate(), która wymusza zniszczenie i ponowne utworzenie Activity, ale to jest ponowne utworzenie nowego egzemplarza.
Jeśli nie wywołasz super.onCreate(savedInstanceState), Android Runtime wyrzuci wyjątek SuperNotCalledException i aplikacja ulegnie awarii. System ściśle wymaga, aby każda nadpisana metoda cyklu życia wywoływała swoją super-wersję — gwarantuje to poprawne działanie wewnętrznego automatu stanów.
Główna różnica: onCreate w Activity ładuje UI przez setContentView, a onCreate we Fragment tylko inicjalizuje dane. Fragment tworzy View w osobnej metodzie onCreateView, która może być wywoływana wielokrotnie (na przykład przy przełączaniu zakładek), podczas gdy onCreate Fragment jest wywoływany raz na czas życia egzemplarza Fragment.
Dane zainicjalizowane w onCreate przechowuje się w polach klasy Activity lub Fragment. Na przykład private lateinit var binding: ActivityMainBinding jest deklarowane na poziomie klasy, inicjalizowane w onCreate i dostępne we wszystkich kolejnych metodach. Dla danych przetrwających obrót ekranu używa się ViewModel z LiveData lub StateFlow.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również