A ViewModel launches `viewModelScope.launch { val a = async { fetchA() }; val b = async { fetchB() }; combine(a.await(), b.await()) }`. `fetchB()` throws. What happens?
- A. Only `b.await()` throws; `a` keeps running and the parent coroutine survives, logging the error
- B. The exception from `fetchB()` propagates, cancels the parent coroutine (and sibling `a`), and crashes unless a handler is installed on the scope ✓
- C. `async` swallows the exception until `a.await()` is also called, so nothing happens
- D. A `CoroutineExceptionHandler` on `async` catches it and resumes normally
Correct answer: B. Because these `async` builders inherit the regular `Job` of `viewModelScope`, a child failure propagates upward, cancelling the parent and sibling regardless of when `await()` is called.
A Compose list item recomposes on every scroll frame even when its data is unchanged. The item takes `List<Item>` and a `() -> Unit` lambda as parameters. What is the MOST likely root cause and fix?
- A. `List<Item>` is inferred unstable, so the composable is never skippable; use `ImmutableList`/`@Immutable` or `kotlinx.collections.immutable` ✓
- B. The lambda allocates each recomposition; wrapping it in `remember` fixes stability of the list too
- C. `derivedStateOf` must wrap the list read to make it observable and skippable
- D. Marking the item `@Composable @Stable` forces Compose to skip regardless of parameter stability
Correct answer: A. The Compose compiler treats `List` as unstable (it could be a mutable implementation), making the function non-skippable; using a provably-immutable collection type restores skippability.
You call `stateFlow.collect { }` inside a `Fragment.onViewCreated` using `lifecycleScope.launch { ... }` (not `repeatOnLifecycle`). What is the concrete bug?
- A. Collection stops at `onStop` and correctly restarts at `onStart`, so it is safe
- B. StateFlow is cold, so nothing is ever collected without `repeatOnLifecycle`
- C. The coroutine keeps collecting while the Fragment is in the background and holds the destroyed View, wasting work and risking a leak ✓
- D. `StateFlow` conflates emissions, so background collection is automatically paused
Correct answer: C. `lifecycleScope.launch` only cancels at `onDestroy`, so the collector stays active in the background updating a stopped/destroyed view; `repeatOnLifecycle(STARTED)` is what pauses and resumes it.
Two offline devices edit the same record's different fields while disconnected, then both sync. The backend must remain single source of truth and no user edit should be silently lost. Which strategy best satisfies this?
- A. Last-write-wins by wall-clock timestamp on the whole record
- B. Per-field merge / CRDT-style reconciliation on the server so non-conflicting field edits both survive ✓
- C. Reject the later sync with a 409 and force the user to re-enter everything
- D. Vector clocks that only detect conflicts but always discard the older sibling
Correct answer: B. Whole-record LWW loses one device's field edit; per-field (or CRDT) merge lets independent field changes coexist while the server stays authoritative for true conflicts.
In Swift, a `URLSession` completion closure captures `self` and stores the returned task in a `self.task` property. Even after the request finishes, the object leaks. Why, and what is the minimal fix?
- A. `URLSession` is a singleton so it always retains delegates; switch to `.shared`
- B. The closure strongly captures `self` while `self` retains the task via `self.task`, forming a retain cycle; use `[weak self]` in the capture list ✓
- C. ARC cannot free `URLSessionTask`; call `task.cancel()` in `deinit`
- D. Completion closures are always `@escaping` and leak by design; mark it non-escaping
Correct answer: B. `self` → `task` → closure → `self` is a strong reference cycle; capturing `[weak self]` breaks it so ARC can deallocate once the request completes.
Under Swift 6 strict concurrency, you have a mutable cache accessed from many `Task`s. Which primitive gives compile-time data-race safety with the least boilerplate?
- A. A `class` guarded by a manual `NSLock` around every access
- B. An `actor` wrapping the cache, so all mutable access is serialized and isolated ✓
- C. A `DispatchQueue.sync` barrier on a concurrent queue
- D. Marking the cache `@unchecked Sendable` and hoping callers serialize
Correct answer: B. An `actor` provides compiler-enforced isolation and serialized access to its mutable state, which is exactly what strict concurrency checking rewards, without manual locking.
A React Native (old architecture) screen janks when a native module returns a large payload consumed on scroll. Why does JSI/TurboModules fix it where the legacy bridge could not?
- A. JSI adds more worker threads to the bridge queue so serialization is parallelized
- B. The legacy bridge serializes all calls to JSON and batches them async over a single queue; JSI lets JS hold direct synchronous references to native objects, removing the serialization bottleneck ✓
- C. TurboModules move all JS execution onto the UI thread, eliminating frame drops
- D. Fabric compresses the JSON payload so it crosses the bridge faster
Correct answer: B. The old bridge's cost is asynchronous JSON serialization over one queue; JSI exposes native objects to the JS engine directly (synchronously, no JSON), which TurboModules/Fabric build on to remove that bottleneck.
An Android app is killed under memory pressure while a form is half-filled. On relaunch the user lands back with an empty form. `onSaveInstanceState` was implemented but data still lost. Which is the correct explanation?
- A. `onSaveInstanceState` is only for config changes; process death also needs the saved Bundle to be restored in `onCreate`/`SavedStateHandle`, and large data exceeding Binder limits is dropped ✓
- B. Process death never triggers `onSaveInstanceState`, so no state can ever be saved
- C. `ViewModel` survives process death and should have held the form state
- D. Restoration fails because `onRestoreInstanceState` runs before `onCreate`
Correct answer: A. `onSaveInstanceState` does fire before process death, but restored state must be read back (via Bundle/SavedStateHandle) and stay within the ~1MB Binder transaction limit or it is silently dropped; ViewModels do NOT survive process death.
A coroutine wrapped in `withContext(NonCancellable)` or doing a tight CPU loop `while(true){}` ignores `job.cancel()`. Why doesn't it stop, and what is the correct fix for the CPU-loop case?
- A. Cancellation is cooperative; a busy loop never suspends or checks `isActive`, so add `ensureActive()`/`yield()` or check `isActive` in the loop ✓
- B. `cancel()` only sets a flag for structured concurrency and never affects running code
- C. Dispatchers.Default disables cancellation for performance
- D. The loop needs `SupervisorJob` to become cancellable
Correct answer: A. Coroutine cancellation is cooperative and only takes effect at suspension/check points, so a non-suspending busy loop must explicitly call `yield()`/`ensureActive()` or test `isActive`.
You want a `Flow` that (a) has no initial value, (b) never replays to new subscribers, and (c) drops events if there are momentarily no collectors, for one-shot navigation events. Which construct fits?
- A. `StateFlow` with an initial dummy value
- B. `SharedFlow(replay = 0, extraBufferCapacity = 0, onBufferOverflow = SUSPEND)` — with an active collector, or `Channel` for strict single-consumer delivery ✓
- C. `StateFlow(null)` mapped with `filterNotNull`
- D. `SharedFlow(replay = 1)`
Correct answer: B. `replay = 0` prevents redelivery to late subscribers (StateFlow always replays its latest), matching one-shot event semantics; a `Channel` is the stricter single-consumer alternative.