Wrong thread in cancelation when removeListener() in AndroidTest
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 3k
- Forks
- 955
- Avg merge
- 12d 14h
- Merged PRs (30d)
- 2
Description
Version
Media3 1.9.0 and 1.8.0
More version details
We have the following problem:
Every time a test fails and the player has been initialised, we get the following exception:
java.lang.IllegalStateException: Player is accessed on the wrong thread.
Current thread: “Instr: eu.xxx.app.testutils.helper.XxxAndroidTestRunner”
Expected thread: 'main'
We are running an Android test. The app is partly in Compose and partly in XML Views.
In Compose, we have integrated ExoPlayer with:
val exoPlayer = remember {
ExoPlayer.Builder(context)
.setLooper(android.os.Looper.getMainLooper())
.build().apply {
prepare()
}
}
The problem seems to be this code.
That code is part of the media3 PlayerExtensionsKt.class.
private suspend fun Player.listenImpl(
events: Player.Events?,
onEvents: Player.(Player.Events) -> Unit,
): Nothing {
lateinit var listener: PlayerListener
try {
suspendCancellableCoroutine { continuation ->
listener = PlayerListener(events, onEvents, continuation)
continuation.invokeOnCancellation { listener.isCancelled.set(true) }
addListener(listener)
}
} finally {
removeListener(listener)
}
}
Normally, the code is only accessible via a check on the main thread.
However, if there is a cancellation, then finally is called with the AndroidTestRunner thread because the cancellation comes from the thread. This causes the check in removeListener() to fail and throws the wrong thread exception.
Im an not sure, if it is the code or if i do something wrong. Any ideas how to fix that?
Devices that reproduce the issue
Emulator Android 15
But others aswell
Devices that do not reproduce the issue
No response
Reproducible in the demo app?
Not tested
Reproduction steps
I'm not sure how to set this up with minimal effort.
Expected result
It should not crash in failed ui tests
Actual result
java.lang.IllegalStateException: Player is accessed on the wrong thread.
Current thread: 'Instr: eu.xxx.app.testutils.helper.XxxAndroidTestRunner'
Expected thread: 'main'
See https://developer.android.com/guide/topics/media/issues/player-accessed-on-wrong-thread
at androidx.media3.exoplayer.ExoPlayerImpl.verifyApplicationThread(ExoPlayerImpl.java:3102)
at androidx.media3.exoplayer.ExoPlayerImpl.removeListener(ExoPlayerImpl.java:1918)
at androidx.media3.common.PlayerExtensionsKt.listenImpl(PlayerExtensions.kt:131)
at androidx.media3.common.PlayerExtensionsKt.access$listenImpl(PlayerExtensions.kt:1)
at androidx.media3.common.PlayerExtensionsKt$listenImpl$1.invokeSuspend(Unknown Source:13)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at androidx.compose.ui.test.ApplyingContinuationInterceptor$SendApplyContinuation.resumeWith(ApplyingContinuationInterceptor.kt:52)
at androidx.compose.ui.test.FrameDeferringContinuationInterceptor$FrameDeferredContinuation.resumeWith(FrameDeferringContinuationInterceptor.jvm.kt:187)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:163)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:152)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:470)
at kotlinx.coroutines.CancellableContinuationImpl.cancel(CancellableContinuationImpl.kt:214)
at kotlinx.coroutines.CancellableContinuationImpl.parentCancelled$kotlinx_coroutines_core(CancellableContinuationImpl.kt:221)
at kotlinx.coroutines.ChildContinuation.invoke(CancellableContinuationImpl.kt:698)
at kotlinx.coroutines.JobSupport.notifyCancelling(JobSupport.kt:1604)
at kotlinx.coroutines.JobSupport.tryMakeCancelling(JobSupport.kt:826)
at kotlinx.coroutines.JobSupport.makeCancelling(JobSupport.kt:786)
at kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core(JobSupport.kt:702)
at kotlinx.coroutines.JobSupport.parentCancelled(JobSupport.kt:668)
at kotlinx.coroutines.ChildHandleNode.invoke(JobSupport.kt:1580)
at kotlinx.coroutines.JobSupport.notifyCancelling(JobSupport.kt:1604)
at kotlinx.coroutines.JobSupport.tryMakeCompletingSlowPath(JobSupport.kt:931)
at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:894)
at kotlinx.coroutines.JobSupport.cancelMakeCompleting(JobSupport.kt:727)
at kotlinx.coroutines.JobSupport.cancelImpl$kotlinx_coroutines_core(JobSupport.kt:698)
at kotlinx.coroutines.JobSupport.cancelInternal(JobSupport.kt:663)
at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:648)
at kotlinx.coroutines.Job$DefaultImpls.cancel$default(Job.kt:207)
at androidx.compose.runtime.Recomposer.cancel(Recomposer.kt:1208)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invokeSuspend(ComposeUiTest.android.kt:1001)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(Unknown Source:8)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1$1$1$1.invoke(Unknown Source:2)
at androidx.compose.ui.test.EspressoLink.withStrategy(EspressoLink.android.kt:69)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invokeSuspend(ComposeUiTest.android.kt:602)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(Unknown Source:8)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment$runTest$1$1.invoke(Unknown Source:4)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$1.invokeSuspend(TestBuilders.kt:317)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at kotlinx.coroutines.test.TestDispatcher.processEvent$kotlinx_coroutines_test(TestDispatcher.kt:24)
at kotlinx.coroutines.test.TestCoroutineScheduler.tryRunNextTaskUnless$kotlinx_coroutines_test(TestCoroutineScheduler.kt:99)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$2$1$workRunner$1.invokeSuspend(TestBuilders.kt:326)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:34)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:100)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:263)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:94)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:70)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source:1)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:48)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersJvmKt.createTestResult(TestBuildersJvm.kt:10)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:309)
at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTest-8Mi8wO0(TestBuilders.kt:167)
at kotlinx.coroutines.test.TestBuildersKt.runTest-8Mi8wO0(Unknown Source:1)
at androidx.compose.ui.test.AndroidComposeUiTestEnvironment.runTest(ComposeUiTest.android.kt:585)
at androidx.compose.ui.test.junit4.AndroidComposeTestRule$apply$1.evaluate(AndroidComposeTestRule.android.kt:334)
at eu.xxx.app.testutils.rules.PrepareAppRule$apply$1.evaluate(PrepareAppRule.kt:34)
at eu.xxx.app.testutils.rules.AllureInformationRule$apply$1.evaluate(AllureInformationRule.kt:18)
at androidx.test.rule.GrantPermissionRule$RequestPermissionStatement.evaluate(GrantPermissionRule.java:150)
at eu.xxx.app.testutils.rules.DisableAnimationsRule$apply$1.evaluate(DisableAnimationsRule.kt:20)
at com.adevinta.android.barista.rule.cleardata.ClearDatabaseRule$apply$1.evaluate(ClearDatabaseRule.kt:31)
at com.adevinta.android.barista.rule.cleardata.ClearPreferencesRule$1.evaluate(ClearPreferencesRule.java:26)
at io.qameta.allure.android.rules.LogcatRule$apply$1.evaluate(LogcatRule.kt:23)
at io.qameta.allure.android.rules.ScreenshotRule$apply$1.evaluate(ScreenshotRule.kt:22)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:68)
at androidx.test.internal.runner.TestExecutor.execute(TestExecutor.java:59)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:467)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2594)
Media
No Media
Bug Report
- You will email the zip file produced by
adb bugreportto android-media-github@google.com after filing this issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.