Почему установка UI происходит в onCreate, а не в onStart в Android
Что проверяют
Понимаете ли вы, чем состояние «создана» отличается от состояния «видима», и по какому признаку код вообще раскладывают по методам жизненного цикла. Критерий один: сколько раз метод вызывается за жизнь экземпляра.
Как отвечать
onCreate() вызывается один раз при создании активити, onStart() каждый раз, когда она становится видимой: пара onStop() и onStart() повторяется при каждом возврате из фона. Разметка создаётся один раз, поэтому setContentView() и получение ссылок на View живут в onCreate(). Перенесите их в onStart(), и иерархия будет инфлейтиться заново при каждом возврате: лишняя работа на главном потоке, сброшенные поля ввода и прокрутка, задублированные подписки на обработчики.
Второй аргумент про восстановление состояния. В onCreate() приходит savedInstanceState, а состояние View система восстанавливает после onStart() и до onResume(). К этому моменту иерархия обязана существовать, иначе восстанавливать нечего.
Отсюда рабочее правило: в onCreate() то, что делается один раз (разметка, биндинг, ViewModel, адаптеры), в onStart() и onStop() то, что привязано к видимости (подписки на датчики и геолокацию, регистрация ресиверов, сбор Flow через repeatOnLifecycle).
Где спотыкаются
Объясняют через то, что в onStart() активити уже видна и потому поздно. Пользователь не видит ничего до первого кадра, который рисуется уже после onResume(), дело не в этом. Настоящая причина в числе вызовов: onStart() повторяется, onCreate() нет.
Складывают в onCreate() вообще всё: сетевые запросы, тяжёлую инициализацию, подписки. Это главный поток и задержка первого кадра, а подписка, сделанная в onCreate() и не снятая в onStop(), продолжает жить в фоне и течёт.
Забывают, что onCreate() вызывается снова при смене конфигурации и после убийства процесса, поэтому состояние нельзя держать в полях активити: ему место в ViewModel или в savedInstanceState. Отдельная ловушка у фрагментов: там UI создаётся в onCreateView(), а View живёт меньше самого фрагмента, поэтому биндинг обнуляют в onDestroyView().