onCreate is the first and only mandatory method of the Activity and Fragment lifecycle in Android. The system calls it once when creating a component, passing the Bundle parameter with previously saved state. Inside onCreate, the developer initializes the user interface, binds View elements, configures event handlers, and restores data from savedInstanceState. Without a correct implementation of onCreate, no Android application can launch — it is the entry point for each screen. For more details about the general Activity lifecycle, read the article Activity Lifecycle.
Key Takeaways
onCreate is a callback method that Android calls when creating a new instance of an Activity or Fragment. This is the first entry point into the user screen code: no user code runs before onCreate is called. The system passes a Bundle parameter to the method, which either contains previously saved data (when recreating) or is null (on first launch).
The onCreate method is defined in the android.app.Activity class and the androidx.fragment.app.Fragment class. Both variants perform similar tasks: component initialization, UI setup, and state restoration. However, the specific implementation differs — Activity uses setContentView to load the layout, while Fragment returns a View through onCreateView. The developer must override at least onCreate in Activity — without this, Android cannot display the screen.
onCreate is called strictly once per full lifecycle of an Activity instance. Even on screen rotation, a new Activity instance receives a new onCreate call with the Bundle from the previous instance. This makes onCreate the ideal place for one-time initialization: loading data, creating adapters, setting up DI components via Dagger or Hilt.
In Activity, the onCreate method performs four key tasks: loading layout markup, initializing View elements, restoring state from Bundle, and setting up primary event handlers. The mandatory minimum code in onCreate is calling super.onCreate(savedInstanceState) and setContentView(R.layout.activity_main).
class MainActivity : AppCompatActivity() {
private var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ViewBinding — a modern replacement for findViewById
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding?.root)
// Initialization using binding
binding?.apply {
welcomeText.text = getString(R.string.welcome)
startButton.setOnClickListener { startGame() }
}
// State restoration
if (savedInstanceState != null) {
score = savedInstanceState.getInt("score", 0)
binding?.scoreText?.text = score.toString()
}
}
}
Modern practice uses ViewBinding instead of findViewById. ViewBinding generates the ActivityMainBinding class at compile time, which eliminates errors with incorrect IDs and reduces boilerplate code. Google recommends ViewBinding as the standard way to access View in Activity and Fragment starting with Android Studio 3.6.
The order of operations in onCreate must be strict: first super, then setContentView, then everything else. Calling findViewById before setContentView returns null — the layout has not yet been loaded, and View elements do not exist in the hierarchy. This is one of the most common mistakes made by beginning Android developers.
onCreate in Fragment differs from Activity: setContentView is not called here, only data initialization not related to UI is performed. Fragment separates component creation and View creation into two distinct methods: onCreate (called once) and onCreateView (called each time the View is created or recreated).
class UserListFragment : Fragment() {
private lateinit var viewModel: UserViewModel
private var binding: FragmentUserListBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// ViewModel initialization — survives View recreation
viewModel = ViewModelProvider(this)[UserViewModel::class.java]
// Arguments from FragmentManager
arguments?.let {
viewModel.loadUser(it.getString("user_id") ?: "")
}
// Saving on rotation
retainInstance = true
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentUserListBinding.inflate(inflater, container, false)
return binding!!.root
}
}
The key difference between Activity and Fragment onCreate: onCreate in Fragment should not contain code related to View, because the View can be destroyed and recreated (for example, when switching ViewPager tabs), while onCreate is called only once. Data loading, ViewModel setup, and adapter initialization are onCreate tasks, while View binding is a task for onViewCreated.
The savedInstanceState parameter in onCreate is a mechanism for saving and restoring the temporary state of an Activity or Fragment. When the system destroys an Activity (screen rotation, low memory), it calls onSaveInstanceState(), where the developer puts key-value entries into a Bundle. When a new instance is created, this Bundle is returned in onCreate.
Bundle supports the following data types: String, Integer, Boolean, Long, Float, Double, their arrays, as well as Parcelable and Serializable objects. For complex objects, Parcelable is used — a more performant serialization mechanism specific to Android. The Bundle size is limited to approximately 500 KB — exceeding the limit causes a 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)
}
It is important to understand: onSaveInstanceState is not called when the user explicitly closes the Activity via finish() or the Back button. The system considers that the user is consciously ending the work and does not need to save state. Therefore, you cannot rely solely on savedInstanceState for long-term data storage — use Room, DataStore, or SharedPreferences.
onCreate executes on the main (UI) thread, and the system waits for it to complete before displaying the Activity on screen. If onCreate takes longer than 5 seconds, the system shows an ANR (Application Not Responding) dialog and offers the user the option to close the application. Long operations, such as loading data from the network or reading from a database, must be moved to a background thread.
According to Google Android Performance (2025) recommendations, onCreate should complete in less than 1 second on mid-range devices. To achieve this: use lazy initialization (lazy delegate in Kotlin), defer heavy data loading to onResume or via coroutines, apply ViewStub for rarely used UI components, and profile startup time through Android Vitals.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Lazy initialization — object is created only on first access
val heavyData by lazy {
HeavyDataLoader.load()
}
// Loading data in background thread via lifecycleScope
lifecycleScope.launch(Dispatchers.IO) {
val users = userDao.getAllUsers()
withContext(Dispatchers.Main) {
adapter.submitList(users)
}
}
}
Profiling tools: Android Studio Profiler (CPU tab) shows the exact execution time of each method. In Android Vitals (Google Play Console), you can track the “Cold start time” metric — if your Activity’s onCreate exceeds 500 ms, the console marks it as a performance issue. At IT Sectr, we use Macrobenchmark tests for automatic control of each Activity’s startup time in the CI pipeline.
ViewModel is the best way to initialize data in onCreate that should survive screen rotation. ViewModel is created in onCreate via ViewModelProvider and is automatically preserved on configuration changes. When an Activity is recreated after rotation, the ViewModel remains in memory, and onCreate receives the same ViewModel without data loss.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
// ViewModel is created once and survives configuration changes
val viewModel: ProfileViewModel =
ViewModelProvider(this)[ProfileViewModel::class.java]
// LiveData observation — UI automatically updates when data changes
viewModel.user.observe(this) { user ->
binding?.userName?.text = user.name
binding?.userEmail?.text = user.email
}
// Loading data if ViewModel was just created
if (savedInstanceState == null) {
viewModel.loadProfile(userId)
}
}
The combination of ViewModel + LiveData/StateFlow solves the screen rotation problem without manual saving to Bundle. ViewModel stores data in memory, LiveData automatically re-subscribes the Activity on recreation, and StateFlow (from Kotlin Coroutines) adds reactivity with coroutine support. This is the standard architecture recommended by Google in the Guide to App Architecture.
Even experienced developers make typical mistakes in onCreate. Let’s look at the five most common problems and how to avoid them.
The most common mistake is trying to find a View via findViewById before calling setContentView. All View elements are created at the moment of layout inflation, so any findViewById call before setContentView returns null and causes a NullPointerException when trying to use the View. Solution: strict order — first super, then setContentView, then findViewById or ViewBinding.
Loading data from the network, reading from a database, or processing large arrays directly in onCreate blocks the rendering of the first frame. The user sees a black screen until onCreate completes, which worsens the perception of application speed. Solution: use lifecycleScope.launch for asynchronous operations, display a skeleton (placeholder UI) until loading completes.
If you do not restore state from Bundle on screen rotation, the user loses all unsaved input: text in form fields, scroll position, selected items. Solution: always check savedInstanceState != null in onCreate to restore data, even if state loss seems unlikely.
Anonymous classes and lambdas in onCreate can implicitly hold a reference to an Activity after it is destroyed. For example, a Handler created in onCreate continues to execute delayed tasks even after the Activity is destroyed. Solution: use LifecycleObserver, ViewModel, and lifecycleScope, which automatically cancel tasks on destruction.
Initializing View in Fragment onCreate is a logical error, since the View can be recreated without calling onCreate. If you set a listener in onCreate but bind View in onCreateView, the listener will remain on the old View when recreated. Solution: perform all View-related work in onViewCreated, leaving onCreate only for data-layer initialization.
Frequently Asked Questions
Yes, overriding onCreate is mandatory for any Activity that displays a user interface. Without it, it is impossible to call setContentView and load the XML layout. If the Activity has no UI (for example, a transparent stub Activity), onCreate is still overridden, but without calling setContentView.
No, onCreate cannot be called again for the same Activity instance. If the Activity is destroyed and recreated (screen rotation, low memory), it is a new instance with a new call to onCreate. An exception is the recreate() method, which forcefully destroys and recreates the Activity, but this is a recreation of a new instance.
If you do not call super.onCreate(savedInstanceState), Android Runtime throws a SuperNotCalledException and the application crashes. The system strictly requires that every overridden lifecycle method calls its super version — this ensures correct operation of the internal state machine.
The main difference: onCreate in Activity loads the UI via setContentView, while onCreate in Fragment only initializes data. Fragment creates View in a separate method onCreateView, which can be called multiple times (for example, when switching tabs), whereas Fragment onCreate is called once per Fragment instance lifetime.
Data initialized in onCreate is stored in the Activity or Fragment class fields. For example, private lateinit var binding: ActivityMainBinding is declared at the class level, initialized in onCreate, and available in all subsequent methods. For data that survives screen rotation, use ViewModel with LiveData or StateFlow.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also