fix: launch playlist diffing coroutine lazily instead of in init - #1444
fix: launch playlist diffing coroutine lazily instead of in init#1444joelmuraguri wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the ExoplayerController to launch the diffingJob coroutine lazily (CoroutineStart.LAZY) within the init block, starting it explicitly when a video player state is requested. This addresses a TODO and removes an unnecessary cancellation call. The reviewer suggested declaring diffingJob as a non-nullable val instead of a nullable var since it is never reassigned, which would clean up the code by removing null-safe navigation operators.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| isLooping: Boolean, | ||
| autoplay: Boolean, | ||
| ): VideoPlayerState { | ||
| diffingJob?.start() |
There was a problem hiding this comment.
Since diffingJob is initialized in the init block and never reassigned, it can be declared as a non-nullable val instead of a nullable var (i.e., private val diffingJob: Job).\n\nThis allows you to safely remove the null-safe navigation operator (?.) here and in teardown(), making the code cleaner and more idiomatic.\n\nPlease also update the declaration of diffingJob (which is outside this diff) to:\nkotlin\nprivate val diffingJob: Job\n
| diffingJob?.start() | |
| diffingJob.start() |
|
I was hoping to remove the |
Summary
ExoplayerController's diffing coroutine: it was launched eagerly ininit, meaning it started running as soon as anExoplayerControllerwas constructed, before any video was ever registered or played.Change
diffingJobis now launched withCoroutineStart.LAZYininit,so the coroutine is built but not scheduled to run.
registerVideothe first call site that can ever send intomediaItemMutationsChannelnow callsdiffingJob?.start()beforedoing anything else.
Job.start()is idempotent, so this is safe to call on everyregisterVideoinvocation.diffingJob?.cancel()that preceded the job's only assignment (dead code,diffingJobwas alwaysnullthere).Why this matters
mediaItemMutationsChannelis a rendezvous channel (Channel<...>(), capacity 0). This makes.start()a hard requirement, not a nice-to-have:if the job were marked lazy without ever calling.start(), the firstsendinregisterVideowould suspend forever with nobody collecting,silently dropping the video from the playlist. This PR wires up both
halves of that contract.
teardown()'sdiffingJob?.cancel()needs no changes cancelling ajob that was never started is already safe.
Out of scope
initblock (the twosnapshotFlow{...}.launchIn(...)flows for pause-on-idle and mute) is a related but separate cleanup.launchInhas no lazy variant, so making those lazy requires rewriting them as plainscope.launch(start = CoroutineStart.LAZY) { flow.collect {...} }, which felt like a separate, slightly bigger change. Happy to follow up with that in asecond PR if desired.
Testing
registerVideoplaystill populates and plays the ExoPlayer playlist as before.registerVideomultiple times does not create multiple diffing collectors (Job.start()no-ops after the first call).teardown()immediately after construction (no video ever registered) cancels cleanly with no exceptions.