Activity

https://developer.android.com/guide/components/activities/intro-activities
https://d.android.com/guide/components/activities/activity-lifecycle
https://developer.android.com/guide/components/activities/state-changes
https://developer.android.com/guide/components/activities/testing
https://developer.android.com/guide/components/activities/tasks-and-back-stack
https://developer.android.com/guide/components/activities/recents
https://d.android.com/guide/components/activities/background-starts
Activity

Основной компонент Android, представляющий экран приложения. Она управляет UI, жизненным циклом и взаимодействием с пользователем.

class MainActivity: AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
    }

    override fun onResume() {
        super.onResume()
    }

    override fun onPause() {
        super.onPause()
    }

    override fun onStop() {
        super.onStop()
    }

    override fun onDestroy() {
        super.onDestroy()
    }
}
onCreate

Вызывается при создании Activity. В нём задаётся интерфейс и начальная логика.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
}
onStart

Вызывается, когда Activity становится видимой для пользователя.

override fun onStart() {
    super.onStart()
    dataObserver.startObserving()
}
onResume

Вызывается, когда Activity переходит в активное состояние и готова взаимодействовать с пользователем.

override fun onResume() {
    super.onResume()
    startRefreshingData()
}
onPause

Вызывается, когда Activity уходит на второй план, но всё ещё частично видима.

override fun onPause() {
    super.onPause()
    pauseAnimations()
}
onStop

Вызывается, когда Activity перестаёт быть видимой.

override fun onStop() {
    super.onStop()
    dataObserver.stopObserving()
}
onDestroy

Вызывается перед уничтожением Activity.

override fun onDestroy() {
    super.onDestroy()
    cleanupResources()
}
isFinishing

Используется для проверки, находится ли активность в процессе завершения. Он возвращает true, если активность была запущена для завершения (например, из-за вызова finish() или по завершению жизненного цикла Activity), и false, если активность все еще активно работает. Вы можете использовать isFinishing() перед выполнением операций, которые требуют, чтобы активность была активна, чтобы избежать потенциальных ошибок, связанных с обращением к UI, если активность уже завершена.

override fun onPause() {
    super.onPause()
    if (!isFinishing) {
        // Выполняем действия только если Activity не завершается
    }
}
Launch Modes
standart

Режим запуска Activity по умолчанию в Android. В этом режиме каждый раз, когда Activity запускается, создается новый экземпляр этой Activity, и каждый новый экземпляр помещается в стек задач. При этом Activity может быть создано несколько раз, даже если уже существует экземпляр этой Activity в стеке. Каждый новый экземпляр Activity в режиме standard получает уникальный task и back stack, что позволяет сохранить состояние и историю переходов, обеспечивая независимость между различными экземплярами Activity.

<activity
    android:name=".MainActivity"
    android:launchMode="standart" />
singleTop

Режим запуска Activity, при котором только один экземпляр Activity может быть в стеке задач. Если Activity с singleTop уже находится на вершине стека, новая попытка её запуска не создаст новый экземпляр, а вызовет метод onNewIntent(). Если Activity не на вершине стека, будет создан новый экземпляр.

<activity
    android:name=".MainActivity"
    android:launchMode="singleTop" />
singleTask

Режим запуска Activity, при котором в одной задаче может существовать только один экземпляр Activity. Если Activity с singleTask уже существует, она будет показана, и все Activity в текущем стеке выше её будут удалены; если нет, она будет создана в новой задаче.

<activity
    android:name=".MainActivity"
    android:launchMode="singleTask" />
singleInstance

Устанавливает, что Activity будет существовать только в одном экземпляре в своей собственном, отдельном стеке задач (task). Это означает, что если такая Activity уже существует, то новая задача не создаст её экземпляр, а вместо этого вернёт существующий экземпляр.

<activity
    android:name=".MainActivity"
    android:launchMode="singleInstance" />
singleInstancePerTask

Когда активность запускается в режиме singleInstancePerTask, система создает единственный экземпляр этой Activity для каждой задачи. Это означает, что если активность уже существует в другой задаче, новый экземпляр не будет создан. Вместо этого пользователь будет перенаправлен к существующему экземпляру. В отличие от других режимов запуска (например, singleTask), этот режим обеспечивает, что Activity всегда будет единственной в своей задаче, и только одна задача может содержать этот экземпляр. Если Activity вызывается из другой задачи, она будет перемещена в фокус, а не создана заново.

• Добавлен в Android 12 (API 31).

<activity
    android:name=".MainActivity"
    android:launchMode="singleInstancePerTask" />
taskAffinity

Определяет, к какой задаче (task) принадлежит активность. По умолчанию все активности приложения имеют одинаковую задачу, но с помощью taskAffinity можно задать для активности принадлежность к другой задаче. Task (Задача) — это стек активностей, с которыми взаимодействует пользователь. Если активность имеет другую taskAffinity, она может быть запущена в отдельной задаче или присоединиться к задаче другого приложения с таким же значением taskAffinity. Это полезно, когда нужно контролировать, как активности взаимодействуют с задачами и как они отображаются в списке последних приложений.

• Работает в связке с launchMode активности (например, singleTask, singleTop). Например, если taskAffinity изменён, а launchMode задан как singleTask, активность будет запускаться в своей задаче и перезапускаться, если она уже существует.

• Хорошее объяснение на StackOverflow.

<activity
    android:name=".MainActivity"
    android:taskAffinity="" />
    
<activity
    android:name=".SettingsActivity"
    android:taskAffinity="com.example.differenttask" />
TaskStackBuilder

Позволяет создавать и управлять стеком задач для запуска активностей. Он помогает организовать задачи и их историю, чтобы обеспечить правильное поведение навигации при запуске новых активностей. TaskStackBuilder упрощает создание и запуск стека активностей, который будет отображаться в правильном порядке, когда пользователь возвращается к предыдущим активностям.

// Создаем TaskStackBuilder для запуска нового Activity
val stackBuilder = TaskStackBuilder.create(context)

// Добавляем Intent для нового Activity
stackBuilder.addNextIntentWithParentStack(newIntent)

// Запускаем Activity
stackBuilder.startActivities()
addNextIntent

Добавляет новый Intent в back stack без учета родительских экранов.

addNextIntentWithParentStack

Добавляет новый Intent в back stack с учетом иерархии родительских экранов.

ActivityManager

Системный сервис в Android, который управляет жизненным циклом активностей и задач, а также отслеживает состояние процессов в системе. ActivityManager отслеживает все активные, видимые и фоновые активности на устройстве.

val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
getRunningTasks

Возвращает список задач, которые в настоящее время запущены. Можно указать максимальное количество задач, которые нужно вернуть.

val runningTasks = activityManager.getRunningTasks(10)
getRunningAppProcesses

Возвращает список всех запущенных процессов в системе.

val runningAppProcesses = activityManager.runningAppProcesses
getRecentTasks

Возвращает список недавних задач, которые можно открыть из недавнего меню приложений.

val recentTasks = activityManager.getRecentTasks(10, ActivityManager.RECENT_IGNORE_UNAVAILABLE)
getRunningServices

Возвращает список всех запущенных служб.

val runningServices = activityManager.getRunningServices(10)
getMemoryInfo

Заполняет объект ActivityManager.MemoryInfo информацией о текущем состоянии памяти.

val memoryInfo = ActivityManager.MemoryInfo()
activityManager.getMemoryInfo(memoryInfo)
getProcessMemoryInfo

Возвращает информацию о потреблении памяти для процессов с указанными идентификаторами процессов (PID).

val processMemoryInfo = activityManager.getProcessMemoryInfo(intArrayOf(pid))
killBackgroundProcesses

Завершает все фоновые процессы, связанные с указанным пакетом.

activityManager.killBackgroundProcesses("com.example.myapp")
getAppTasks

Возвращает список задач, связанных с текущими приложениями.

val appTasks = activityManager.appTasks
getLauncherActivities

Возвращает список активностей, которые могут быть запущены из лаунчера.

val launcherActivities = activityManager.launcherActivities
OnBackPressedDispatcher

Управляет обработкой нажатия кнопки «Назад» (Back) в приложении. Позволяет разным частям приложения регистрировать свои обработчики, которые будут вызваны при нажатии кнопки. Пришел на замену методу onBackPressed.

class MainActivity: AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Регистрируем обработчик нажатия кнопки "Назад"
        onBackPressedDispatcher.addCallback(this, object: OnBackPressedCallback(true) {
            override fun handleOnBackPressed() {
                // Логика при нажатии кнопки "Назад"
                println("Кнопка 'Назад' нажата")
            }
        })
    }
}
Вопросы на собесе (33)
  • Lifecycle (14)
    1. Жизненный цикл Activity?

      onCreate() onStart() onResume() onPause() onStop() onDestroy()

    1. Какие методы жизненного цикла Activity вызываются при переходе на следующую Activity?

      Переходим вперед: onPause() onStop()

      Возвращаемся обратно: onStart() onResume()

    1. В какой момент жизненного цикла Activity виден экран?

      onStart()

    1. Какие методы жизненного цикла Activity вызываются при отображении DialogFragment?

      Жизненный цикл не изменяется.

    1. Какие методы жизненного цикла Activity вызываются при отображении/закрытии permission dialog?

      onPause()onResume()

    1. Какие методы жизненного цикла Activity вызываются при переходе в task manager (recent menu)?

      Жизненный цикл не изменяется.

    1. Какие методы жизненного цикла Activity вызываются при смахивании приложения?

      onPause() onStop() onDestroy()

    1. Какие методы жизненного цикла Activity вызываются при сворачивании/разворачивании приложения?

      При сворачивании: onPause() onStop()

      При разворачивании: onStart() onResume()

    1. Какие методы жизненного цикла Activity вызываются при смене ориентации экрана (изменении конфигурации)?

      Activity пересоздается: onPause() onStop()onDestroy()onCreate()onStart()onResume()

      При сохранении состояния: onPause() onStop()onSaveInstanceState() onDestroy()onCreate()onStart()onRestoreInstanceState()onResume()

    1. Если запустить Activity2 которая частично перекрывает Activity1, какие коллбэки вызовутся у Activity1?

      onPause()

    1. Когда onDestroy вызовется без onPause и onStop?

      finish() в onCreate()

    1. В каком случае вызов onDestroy() не гарантирован?

      В случае внезапного завершения процесса системы, например, если система убивает приложение для освобождения ресурсов, когда недостаточно памяти. В таких ситуациях система может не вызвать методы жизненного цикла, включая onDestroy().

    1. Как изменилось поведение onResume и onPause в Android 10?

      В Android 10 добавлена поддержка foldables и изменено поведение коллбэков onResume() и onPause() в режиме multi-window: все видимые активити находятся в состоянии resumed, а Activity, с которой взаимодействует пользователь, называется topmost resumed. Введен коллбэк onTopResumedActivityChanged(isTopResumed: Boolean) для различия между resumed и topmost resumed Activity.

    1. В каком из случаев вызов onDestroy() в Activity не гарантирован?

      Переворот экрана.

      Вызов метода finish().

      Приложение свернуто, система ”убила” процесс приложения.

  • Launch Modes (8)
    1. Какие есть Launch Modes у Activity?

      standart singleTop singleTask singleInstance

    1. Для чего нужно задавать параметр launchMode у Activity?

      Параметр launchMode задает, как будет вести себя новая Activity при запуске, управляя созданием экземпляров в стеке задач и предотвращая дублирование.

    1. Как работает launchMode SingleTop?

      SingleTop запускает Activity только в том случае, если она не находится на вершине стека. Если Activity уже открыта, система вызывает метод onNewIntent() существующего экземпляра, передавая ему новые намерения, что позволяет обновить данные без создания нового экземпляра.

    1. Как работает launchMode SingleInstance?

      SingleInstance создает уникальный экземпляр Activity в своем собственном стекe задач. Если такая Activity уже существует, при запуске вызовется onNewIntent().

    1. Есть стек из Activity A-B-C-A-B, запускаем Activity C c launchMode SingleTop, что произойдет в системе?

      У Activity C вызовется метод onNewIntent().

    1. Ты находишься внутри Activity A и опять вызываешь Activity A c launch mode SingleTop, что произойдет в Activity A?

      Вместо создания нового экземпляра будет переиспользован текущий, и вызовется метод onNewIntent().

    1. Есть Activity А, ты запустил Activity B с launch mode SingleTask и в ней произошел краш, что произойдет с приложением и что увидит юзер?

      Приложение завершится, и пользователь увидит системное сообщение о том, что приложение остановилось. Activity A также будет уничтожена вместе с приложением.

    1. Эффект какого из launch mode Activity описан ниже? Если экземпляр Activity уже существует на вершине текущего Task, новый экземпляр не будет сгенерирован, а новые данные для Activity будут оправлены через onNewlntent().

      singleTask

      singleTop

      singlelnstance

      standard

  • Другие (11)
    1. Как сделать Activity главной?

      Указать для нее в AndroidManifest intent-фильтр с action MAIN и category LAUNCHER.

    1. Что будет если сделать 2 Activity главными?

      Обе будут отображаться в списке приложений, и пользователь сможет выбрать, какую из них запустить.

    1. Как пережить поворот экрана? (Смену конфигурации)

      Переопределить onSaveInstanceState(Bundle outState) и восстановить в onCreate().

      Использовать ViewModel для сохранения данных.

      Добавить configChanges в манифест (не рекомендуется).

    1. Есть ли смысл сохранять данные если поддерживаем только портретную ориентацию?

      Да. В приложении может произойти сбой. Существуют другие способы изменения конфигурации (смена локали).

    1. Как запустить стек из нескольких Activity?

      Через TaskStackBuilder в виде стека одним вызовом.

    1. Как запустить Activity в отдельном процессе?

      Указать атрибут android:process в манифесте для этой Activity.

    1. Чем Activity отличается от Fragment?

      Мы легко можем управлять стеком фрагментов. Со стеком активностей возникают проблемы: мы не можем гарантировать их порядок, активити может создастся по интенту другим приложением.

    1. Если не вызовем setContentView или поместим туда null что увидим?

      Цвет установленный в windowBackground.

    1. Как запустить приложение без Activity?

      Через BroadcastReceiver, который реагирует на системные события (например, загрузку устройства), или через Service, запущенный при старте системы. В этом случае интерфейса у приложения не будет, но фоновые задачи могут выполняться.

    1. Как закрыть сразу n активити в стеке?

      Удалить все Activity выше запущенной в стеке можно через флаг flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK.

    1. Можно ли создать приложение без Activity?

      Да, можно. (Приложение Android WebView. Системный сервис для просмотра web-страниц)

      Нет, у приложения всегда должна быть хотя бы одна активити.