A composable receives `data: List<User>` as a parameter and is never skipped during recomposition, even when the parent recomposes with an identical list reference. All properties of `User` are `val`. What is the MOST likely reason Compose treats this parameter as unstable?
- A. `List<User>` is an interface type that Compose cannot prove immutable, so it infers the parameter as unstable ✓
- B. Passing any `List` always forces a recomposition because collections are compared structurally, not by reference
- C. `User` must be annotated with `@Composable` for the compiler to consider it stable
- D. The composable is missing the `@Stable` annotation on its function declaration
Correct answer: A. `List` is an interface with no immutability guarantee, so the Compose compiler infers it unstable; using `ImmutableList`/`@Immutable` or a `kotlinx.collections.immutable` type fixes it.
Inside a `ViewModel` you write `viewModelScope.launch { val a = async { apiA() }; val b = async { apiB() }; combine(a.await(), b.await()) }`. `apiB()` throws. What happens to `apiA()`'s coroutine and how does the exception surface?
- A. `apiA()` keeps running to completion; the exception is silently swallowed by `async`
- B. `apiA()` is cancelled and the exception propagates to `viewModelScope`, cancelling the whole scope ✓
- C. Only the `async { apiB() }` fails; `a.await()` returns normally and the crash is deferred until `b.await()`
- D. Both `async` calls are cancelled but the exception is caught automatically by `viewModelScope`'s default handler
Correct answer: B. Because the `async` children share the `launch` job under structured concurrency, `apiB`'s failure cancels its siblings (including `apiA`) and propagates up, cancelling `viewModelScope`.
You expose one-time navigation events from a ViewModel using `val events = MutableStateFlow<Event?>(null)` collected in the UI with `collect { ... }`. After a config change (Activity recreated, ViewModel retained), users report the same navigation event fires again. What is the root cause?
- A. `MutableStateFlow` drops emissions, so the re-collection reads a stale buffered event by mistake
- B. `StateFlow` always replays its current value to new collectors, so the retained non-null event is re-delivered on re-subscription ✓
- C. `collect` on the main thread cannot receive events after a config change unless you use `collectLatest`
- D. `MutableStateFlow` requires `distinctUntilChanged` to be disabled to avoid duplicate events
Correct answer: B. `StateFlow` has replay-1 semantics and always emits its current value to new collectors, so retaining a non-null event re-fires it; a `SharedFlow` with replay 0 (or Channel) is the correct primitive for events.
Given `flow { emit(1); emit(2) }.map { heavyCpu(it) }.flowOn(Dispatchers.Default).collect { updateUi(it) }` collected from `Dispatchers.Main`, on which dispatcher does `heavyCpu` run and on which does `updateUi` run?
- A. Both `heavyCpu` and `updateUi` run on `Dispatchers.Default`
- B. `heavyCpu` runs on `Dispatchers.Default`; `updateUi` runs on `Dispatchers.Main` ✓
- C. Both run on `Dispatchers.Main` because `collect` dictates the whole chain's context
- D. `heavyCpu` runs on `Dispatchers.Main`; `updateUi` runs on `Dispatchers.Default`
Correct answer: B. `flowOn` changes context only for upstream operators (the `map` and the source), while the collector runs in the collecting coroutine's context (`Main`).
A `LaunchedEffect(Unit) { while(true) { delay(1000); tick() } }` is placed inside an `if (isVisible)` block. A colleague changes it to `LaunchedEffect(isVisible)` and moves it outside the `if`. What behavioral difference results when `isVisible` toggles true→false→true?
- A. The keyed version restarts `tick()` cleanly on each toggle; the original leaves the coroutine running while invisible ✓
- B. Both versions behave identically because `LaunchedEffect` is keyed by composition position anyway
- C. The keyed version never restarts because `isVisible` is a Boolean, not a stable key
- D. The original version keeps `tick()` running forever even when removed from composition
Correct answer: A. Placing `LaunchedEffect` inside the `if` cancels it when the block leaves composition; keying on `isVisible` outside keeps it in composition but cancels/relaunches whenever the key changes.
Consider: `class Analytics(private val activity: Activity)` stored in a `companion object`-held singleton created from `onCreate`. LeakCanary flags the Activity. What reference chain keeps the Activity alive across rotation?
- A. The `Activity`'s window holds the singleton, forming a cycle the GC cannot collect
- B. The static `companion object` → singleton → `Analytics` → `activity` field, which the GC roots never release across recreation ✓
- C. Rotation creates a new Activity that inherits the old one's context, doubling the leak
- D. `Analytics` implements `LifecycleObserver`, so the lifecycle registry pins the Activity
Correct answer: B. A static (companion) reference is a GC root; because it transitively holds the destroyed Activity via `Analytics.activity`, the Activity can never be collected.
You have `sharedFlow = MutableSharedFlow<Int>(replay = 0, extraBufferCapacity = 0)` with the default `SUSPEND` overflow strategy. A producer calls `sharedFlow.tryEmit(x)` while there are zero active collectors. What is the result?
- A. `x` is buffered and delivered to the next collector that subscribes
- B. `tryEmit` returns `false` and `x` is dropped because there is no buffer or replay to hold it ✓
- C. `tryEmit` suspends until a collector appears
- D. `tryEmit` throws an `IllegalStateException` due to buffer overflow
Correct answer: B. With replay 0 and no buffer, `tryEmit` cannot suspend and has nowhere to place the value with no collectors, so it returns `false` and the value is dropped.
An offline-first app uses Room as the single source of truth. Two devices edit the same record offline; both later sync. To resolve conflicts deterministically while preserving concurrent edits from being silently lost, which strategy best fits a senior design?
- A. Last-write-wins using each device's local wall-clock timestamp on the mutation
- B. Server-assigned version numbers with optimistic concurrency: reject stale writes and surface a merge, using per-field or vector clocks to detect true concurrency ✓
- C. Always prefer the device with the lower device ID to break ties
- D. Disable offline writes on all but one designated primary device
Correct answer: B. Wall clocks skew across devices; server versioning/vector clocks detect genuine concurrent edits and let you merge rather than blindly overwrite, which is the consistency-preserving senior choice.
`collectLatest { item -> processLongRunning(item) }` is applied to a flow emitting rapidly. `processLongRunning` does blocking I/O with no suspension points. What actually happens on fast emissions?
- A. Each new emission cancels and restarts `processLongRunning`, so only the last item's work completes
- B. New emissions cannot cancel the in-flight block because cancellation is cooperative and the blocking call never checks for it ✓
- C. The flow buffers all emissions and runs them sequentially to completion
- D. `collectLatest` throws because the collector cannot keep up with the producer
Correct answer: B. `collectLatest` cancels the previous block on a new emission, but cancellation is cooperative—code with no suspension points never observes the cancel, so it runs to completion regardless.
An app has a cold start of 1.8s. Systrace shows most time in class loading and JIT during the first frames of the main screen. Which remediation most directly targets this symptom?
- A. Move all initialization into a `ContentProvider` to run it earlier
- B. Ship a Baseline Profile so hot code paths are AOT-compiled at install time instead of JIT-compiled at runtime ✓
- C. Increase the heap size in the manifest to reduce GC pauses
- D. Wrap startup work in `WorkManager` with an expedited request
Correct answer: B. Baseline Profiles pre-compile (AOT) the critical startup/scroll code paths at install, eliminating interpretation/JIT overhead that dominates the first frames of cold start.