Skip to main content

Activities, lifecycle, tasks, and back stack

Activity and task solve different problems​

  • Activity: a system-managed user-interface entry point.
  • Task: a user journey represented by an Activity back stack and shown in Recents.
Task: “buy a product”

[CatalogActivity] → [ProductActivity] → [CheckoutActivity]
bottom top

Back normally removes the top destination and reveals the previous one. Home normally moves the task to background. Neither operation necessarily kills the app process.

Lifecycle through a concrete journey​

class CheckoutActivity : AppCompatActivity() {
override fun onCreate(state: Bundle?) {
super.onCreate(state) // Construct UI and restore inputs.
}

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

override fun onResume() {
super.onResume() // Foreground and interactive.
}

override fun onPause() {
// Lose foreground interaction; finish quickly.
super.onPause()
}

override fun onStop() {
// No longer visible.
super.onStop()
}

override fun onDestroy() {
// Instance cleanup; not guaranteed process-final callback.
super.onDestroy()
}
}

If a permission dialog or translucent Activity covers part of the screen, exact callback transitions can differ from a complete navigation away. Design from visibility/interaction requirements and test the real flow.

Rotation versus finish versus process death​

CauseNew Activity later?ViewModelSaved stateMeaning
Configuration changeImmediatelyUsually retainedAvailableSame logical destination
Back/finish()Normally noClearedNot restoration contractUser left destination
Process death while stoppedIf user returnsGoneSmall restorable state may existSystem reclaimed memory

State ownership​

data class CheckoutUiState(
val orderId: String,
val total: Money,
val submitting: Boolean,
val error: String?
)
  • Activity/Compose renders and forwards events.
  • ViewModel coordinates screen state across configuration recreation.
  • SavedStateHandle stores small restoration inputs.
  • Repository/database stores durable order truth.

The Activity should not become the database, network client, and business-rule engine simply because callbacks arrive there.

Default back-stack behaviour​

standard Activity launch normally pushes a new instance:

A → B → B

This is legal. If duplicates are wrong for product UX, first check navigation event duplication. Launch modes are not a substitute for fixing double clicks or repeated effects.

Launch modes​

ModeReuse ruleKey implication
standardAlways new instanceDefault and predictable
singleTopReuse only if target already on topNew Intent goes to onNewIntent()
singleTaskOne task-root-style instance; routes/clears according to task rulesStrong global task effects
singleInstanceIsolated task behaviourRare; surprising UX
singleInstancePerTaskOne instance per taskSpecialized document/task use

Most apps should use default behaviour and Navigation APIs. Advanced launch modes change system task semantics and require tests from launcher, notification, deep link, external app, and Recents.

singleTop corner case​

<activity
android:name=".ProductActivity"
android:launchMode="singleTop" />
class ProductActivity : AppCompatActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
consumeProductId(intent)
}
}

If ProductActivity is already on top, Android does not call onCreate() again. Failing to handle onNewIntent() displays stale product data.

Intent flags interact with launch mode​

  • NEW_TASK asks Android to choose/create an appropriate task.
  • SINGLE_TOP reuses only a matching top instance.
  • CLEAR_TOP removes Activities above an existing target; a standard target may itself be recreated.

Avoid memorizing a slogan like “CLEAR_TOP always reuses.” Predict the starting stack, target launch mode, flags, and expected final stack—then test.

Notification opens myapp://orders/ORD-42 with no process/task.

❌ Avoid dependency on previous screen​

val order = CatalogActivity.selectedOrder

✅ Prefer reconstructable routing​

val orderId = intent.data?.lastPathSegment
?.takeIf { it.matches(Regex("ORD-[0-9]+")) }
?: return finish()

viewModel.load(orderId)

Then apply authentication, authorization, record-exists, and back-stack policy.

Back versus Up​

  • Back returns through actual user history across apps/tasks.
  • Up navigates within the app’s information hierarchy.

They often coincide, but an external deep link can make them different. Up may build/navigate to a parent destination even when that parent was not in the actual history.

Predictive Back​

Modern Android can preview the destination while the gesture progresses. Use AndroidX back APIs/Navigation integration rather than arbitrary onBackPressed() overrides. Custom handling must remain state-consistent and allow system fallback.

Interview-ready answer

An Activity is a UI entry point; a task is the user’s Activity history represented in Recents. Lifecycle callbacks describe visibility and interaction, not guaranteed process lifetime. Default standard launches push instances; launch modes and flags can reuse or reshape tasks, so reused Activities must handle onNewIntent. I use stable route IDs and durable repositories so launcher, notification, deep-link, and process-death entries all reconstruct correctly.

Exercises​

  1. Predict stacks for A→B→C then CLEAR_TOP launch of B with standard and singleTop B.
  2. Design Back and Up after an external order deep link.
  3. Why can fixing duplicate navigation with singleTask create worse bugs?