DevBits
DevBitsAndroid Handbook
Android Fundamentals

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.

AF-009Foundational~45 sec

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 Order object?

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 = 42

The 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 value

Navigation 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

BoundaryTypical mechanismAppropriate data
Activity to ActivityIntent extrasIDs, flags, query text, small values, Uri
Activity to FragmentFragment argumentsData required to create the Fragment
Fragment to FragmentNavigation argumentsRoute data needed by the destination
Sibling Fragments sharing screen stateActivity- or graph-scoped ViewModelState genuinely owned by their shared lifecycle
Durable business dataRepository or databaseThe 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.

Follow-up questions

What separates a senior answer

Key takeaways

Learn more

On this page