Activities and Navigation in Android
Activities and Navigation in Android
An activity is a single entry point into your app, roughly a window the system can launch. Modern apps often use one activity that hosts many Compose screens, and they switch between those screens with Navigation Compose rather than launching a new activity per screen. This article explains the activity lifecycle you must respect, then shows how to navigate between Compose destinations and pass data along.
What an Activity Is
When the user taps your app icon, Android starts your launcher activity. Historically each screen was its own activity. Today the common pattern is a single MainActivity that calls setContent { ... } and then uses navigation inside Compose to show different screens. Fewer activities means simpler state handling.
The Activity Lifecycle
The system calls lifecycle methods as your activity is created, shown, hidden, and destroyed. You do not control exactly when these happen (a phone call, rotation, or low memory can trigger them), so you must handle them gracefully.
The key callbacks, in order of a typical session:
onCreate– set up the UI; called once when the activity is created.onStart– the activity becomes visible.onResume– the activity is in the foreground and interactive.onPause– another screen is partially covering it; save light state here.onStop– no longer visible.onDestroy– being removed from memory.
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Lifecycle", "onCreate") // runs once when created
setContent {
AppNavigation() // host the navigation graph
}
}
override fun onPause() {
super.onPause()
Log.d("Lifecycle", "onPause") // good place to save quick state
}
}
Navigating Between Screens with Navigation Compose
Navigation Compose gives you a NavController and a NavHost that maps string routes to composable screens. You call navController.navigate("route") to move forward and navController.popBackStack() to go back.
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
@Composable
fun AppNavigation() {
// the controller remembers the back stack of screens
val navController = rememberNavController()
// the NavHost declares which composable shows for each route
NavHost(navController = navController, startDestination = "home") {
composable("home") {
// pass a lambda so the screen can request navigation
HomeScreen(onOpenDetails = { navController.navigate("details") })
}
composable("details") {
DetailsScreen(onBack = { navController.popBackStack() })
}
}
}
@Composable
fun HomeScreen(onOpenDetails: () -> Unit) {
Button(onClick = onOpenDetails) { Text("Open details") }
}
@Composable
fun DetailsScreen(onBack: () -> Unit) {
Button(onClick = onBack) { Text("Go back") }
}
Want to learn this properly?
Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.
Browse coursesPassing Data Between Screens
You can include arguments in the route. Keep them small, identifiers rather than whole objects.
// Define a route with a placeholder argument
composable("profile/{userId}") { backStackEntry ->
// read the argument from the back stack entry
val userId = backStackEntry.arguments?.getString("userId")
ProfileScreen(userId = userId)
}
// Navigate with a real value:
// navController.navigate("profile/42")
Pass an ID and let the destination screen load the rest of the data. Do not try to pass large objects through navigation arguments.
Common Mistakes
- Putting setup logic in
onResumethat should be inonCreate.onResumecan run many times; one-time setup belongs inonCreate. - Doing slow work in lifecycle callbacks. Network or disk work on the main thread freezes the UI. Move it off the main thread with coroutines.
- Creating a new activity for every screen. For most apps a single activity with Navigation Compose is simpler and avoids state-passing headaches.
- Passing big objects as navigation arguments. Pass an ID and reload the data. Large arguments are fragile and can crash.
- Forgetting that rotation recreates the activity. Unsaved state is lost unless you store it properly (for example with a ViewModel).
FAQ
Do I need to memorise every lifecycle method? No, but know that the activity can be paused, stopped, and recreated at any time, and design so nothing important is lost.
Is one activity really enough? For many apps, yes. Navigation Compose handles multiple screens within a single activity cleanly.
How do I go back?
Call navController.popBackStack(), or let the system back button do it; Navigation Compose manages the back stack.
Keep Going
To make screens that survive rotation and hold data, you will soon want persistence; see Local Storage in Android. To design the screens themselves, revisit Layouts and UI in Android. The full path is at the Android learning hub.
Want guided practice building multi-screen apps? Join the waitlist for the Android Development course at Infoplanet in Jalgaon.
Want to learn this properly?
Join the waitlist for our courses — beginner-friendly, project-first classes in Jalgaon.
Browse coursesFounder, Infoplanet
Atul Kabra founded Infoplanet in 2001 and has spent over two decades teaching programming — C, C++, Java, databases and more — to students across Maharashtra.
Related guides
Calling an API in Android with Retrofit and Coroutines
How to call a REST API from an Android app using Retrofit and Kotlin coroutines: declare the endpoint, model the JSON, run the call off the main thread, and handle errors.
Layouts and UI in Android with Jetpack Compose
How to build Android screens with Jetpack Compose: stacking with Column and Row, layering with Box, controlling size and spacing with modifiers, and rendering lists efficiently.
Permissions in Android: Requesting Access the Right Way
How Android permissions work: the difference between install-time and runtime (dangerous) permissions, declaring them in the manifest, and requesting them politely in Compose.
