How do you pass data between Activities and Fragments?
Explain how Android components exchange data while preserving clear ownership and a single source of truth.
What the interviewer is testing
This question isn't really about whether you know putExtra() or Fragment arguments. Most interviewers assume you do. They're trying to understand whether you know what information should cross a component boundary, what should remain owned by the source of truth, and how experienced Android engineers keep screens loosely coupled.
A strong answer explains that Activities use Intent extras, Fragments use arguments, and Navigation builds on the same idea. It then goes further: the destination should receive only the information it needs to continue the work, while durable business data remains owned by a repository, database, or another source of truth.
❌ Common mistake
If they ask you to elaborate
Start with ownership, not the API
Imagine an order list opening an order details screen.
The first question is not:
How do I pass the
Orderobject?
It is:
Who owns the order, and what is the smallest stable piece of information the next screen needs?
Usually, the repository owns the Order. The details screen only needs this:
orderId = 42The destination can then load or observe the latest order from the source of truth. Nothing owns the Order except the repository. Navigation simply carries enough information for the next screen to find it.
Order repository
│
│ owns Order data
▼
Order list
│
│ passes orderId
▼
Order details
│
│ loads or observes Order
▼
Current source-of-truth valueNavigation moved the reference, not the ownership. The object itself did not move between screens. Only enough information to continue the work crossed the boundary.
Throughout this example, notice that the destination never receives the Order. It only receives enough information to find the latest one.
This prevents stale copies when the order changes, keeps recreation straightforward, and allows each destination to remain independent.
Choose the mechanism from the boundary
| Boundary | Typical mechanism | Appropriate data |
|---|---|---|
| Activity to Activity | Intent extras | IDs, flags, query text, small values, Uri |
| Activity to Fragment | Fragment arguments | Data required to create the Fragment |
| Fragment to Fragment | Navigation arguments | Route data needed by the destination |
| Sibling Fragments sharing screen state | Activity- or graph-scoped ViewModel | State genuinely owned by their shared lifecycle |
| Durable business data | Repository or database | The actual domain state and source of truth |
The mechanisms differ, but the principle stays the same: pass the smallest stable value that preserves clear ownership.
That value is often an ID, but it may also be a search query, selected filter, enum, date range, or Uri. Small immutable value objects can also be reasonable when the value itself is the navigation input and does not represent shared mutable business state.
Production considerations
Copying large objects between screens creates unnecessary serialization, duplicated state, and stale snapshots. Arguments and extras are intentionally designed for small pieces of transient information, so keep Bundles lightweight and avoid transporting complete object graphs. Large lists, bitmaps, and complex models can also contribute to Binder transaction-size failures.
Passing an ID is also not a magic rule. The destination must be able to handle missing, deleted, or unavailable data. In production, that means loading states, error handling, and a clear fallback when the requested record no longer exists.
Code example
private const val EXTRA_ORDER_ID = "order_id"
class OrderListActivity : AppCompatActivity() {
private fun openOrder(orderId: Long) {
val intent = Intent(this, OrderDetailsActivity::class.java)
.putExtra(EXTRA_ORDER_ID, orderId)
startActivity(intent)
}
}class OrderDetailsViewModel(
savedStateHandle: SavedStateHandle,
repository: OrderRepository
) : ViewModel() {
private val orderId: Long = checkNotNull(savedStateHandle[EXTRA_ORDER_ID])
val order = repository.observeOrder(orderId)
}Notice that the Activity only passes the stable identifier. The important line is repository.observeOrder(orderId): navigation transports the identifier, while the repository remains the single source of truth for the current Order. SavedStateHandle simply exposes the navigation value to the ViewModel.
For a Fragment destination, the same idea applies through Fragment or Navigation arguments rather than Intent extras.