Player is accessed on the wrong thread.
See original GitHub issueExoPlayer Version
2.17.1
Devices that reproduce the issue
Vivo V2027 Android 12 (SDK 31) Vivo V2130 Android 12 (SDK 31) Vivo V2132 Android 12 (SDK 31) Vivo X80 Android 12 (SDK 31) Vivo V2146 Android 11 (SDK 30)
Devices that do not reproduce the issue
POCO X3 Android 12 (SDK 31) Samsung A32 Android 11 (SDK 30) Infinix Note 7 Android 10 (SDK 29) Sony Xperia ZX3 Android 10 (SDK 29)
Reproducible in the demo app?
No
Reproduction steps
none
Expected result
don’t crash
Actual result
exception.class.missing._Unknown_: java.lang.IllegalStateException Player is accessed on the wrong thread.
at android.app.ActivityThread.handleStopService (ActivityThread.java:5021)
at android.app.ActivityThread.access$2400 (ActivityThread.java:284)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2327)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loopOnce (Looper.java:233)
at android.os.Looper.loop (Looper.java:334)
at android.app.ActivityThread.main (ActivityThread.java:8399)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:582)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1068)
Caused by: java.lang.IllegalStateException:
at com.google.android.exoplayer2.ExoPlayerImpl.verifyApplicationThread (ExoPlayerImpl.java)
at com.google.android.exoplayer2.ExoPlayerImpl.getCurrentTimeline (ExoPlayerImpl.java)
at com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector.K (DefaultAnalyticsCollector.java:25)
at com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector.generateEventTime (DefaultAnalyticsCollector.java)
at com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector.generateReadingMediaPeriodEventTime (DefaultAnalyticsCollector.java)
at com.google.android.exoplayer2.analytics.DefaultAnalyticsCollector.onSurfaceSizeChanged (DefaultAnalyticsCollector.java)
at com.google.android.exoplayer2.ExoPlayerImpl.lambda$maybeNotifySurfaceSizeChanged$28 (ExoPlayerImpl.java)
at com.google.android.exoplayer2.ExoPlayerImpl$$InternalSyntheticLambda$0$2d4737b778d7b8503f2f9305c33da9d50db855343d46ce9f1ebd9b998f070724$0.invoke (ExoPlayerImpl.java)
at com.google.android.exoplayer2.util.ListenerSet$ListenerHolder.invoke (ListenerSet.java)
at com.google.android.exoplayer2.util.ListenerSet.lambda$queueEvent$0 (ListenerSet.java)
at com.google.android.exoplayer2.util.ListenerSet$$InternalSyntheticLambda$1$4aa6d67c50ced20c53fd0cecd314136314e77eb5e787a34f9137864aa48c8e5a$0.run (ListenerSet.java)
at com.google.android.exoplayer2.util.ListenerSet.flushEvents (ListenerSet.java:66)
at com.google.android.exoplayer2.util.ListenerSet.sendEvent (ListenerSet.java)
at com.google.android.exoplayer2.ExoPlayerImpl.maybeNotifySurfaceSizeChanged (ExoPlayerImpl.java)
at com.google.android.exoplayer2.ExoPlayerImpl.access$1800 (ExoPlayerImpl.java)
at com.google.android.exoplayer2.ExoPlayerImpl$ComponentListener.surfaceDestroyed (ExoPlayerImpl.java)
at android.service.wallpaper.WallpaperService$Engine.reportSurfaceDestroyed (WallpaperService.java:1945)
at android.service.wallpaper.WallpaperService$Engine.detach (WallpaperService.java:1971)
at android.service.wallpaper.WallpaperService.onDestroy (WallpaperService.java:2400)
at android.app.ActivityThread.handleStopService (ActivityThread.java:5001)
at android.app.ActivityThread.access$2400 (ActivityThread.java:284)
at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2327)
at android.os.Handler.dispatchMessage (Handler.java:106)
at android.os.Looper.loopOnce (Looper.java:233)
at android.os.Looper.loop (Looper.java:334)
at android.app.ActivityThread.main (ActivityThread.java:8399)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:582)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1068)
Code
open class MovieLiveWallpaperService : WallpaperService() {
private var contexts: Activity? = null
enum class Mode {
FIT_CENTER,
FIT_START,
FIT_END,
FIT_XY,
CENTER_CROP
}
private val mode: Mode
get() = AppSharedPreferences
.getStringDefValue(this,"scale_type", Mode.CENTER_CROP.name).let(
Mode::valueOf)
fun setToWallPaper(activity: Activity) {
Utility.sendEvent("Live_Engine")
contexts = activity
try {
val intent = Intent(WallpaperManager.ACTION_CHANGE_LIVE_WALLPAPER)
intent.putExtra(
WallpaperManager.EXTRA_LIVE_WALLPAPER_COMPONENT, ComponentName(
activity,
MovieLiveWallpaperService::class.java
)
)
activity.startActivity(intent)
} catch (ignore: ActivityNotFoundException) {
}
}
override fun onCreateEngine(): Engine = GlEngine()
protected fun initExoMediaPlayer(): ExoPlayer {
val player = this.let { ExoPlayer.Builder(it).build() }
player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
player.playWhenReady = true
player.repeatMode = Player.REPEAT_MODE_ONE
player.volume = 0f
if (mode == Mode.CENTER_CROP) {
player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
} else {
player.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
}
if (mode == Mode.FIT_CENTER) {
player.addListener(object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
contexts?.let {
Toast.makeText(
it,
"${error.message} Something Went Wrong!",
Toast.LENGTH_SHORT
).show()
}
}
})
}
return player
}
inner class GlEngine : WallpaperService.Engine() {
private var exoMediaPlayer = initExoMediaPlayer()
override fun onVisibilityChanged(visible: Boolean) {
exoMediaPlayer.playWhenReady = visible
if (visible) onResume()
else onPause()
}
@Synchronized
override fun onSurfaceCreated(holder: SurfaceHolder) {
kotlin.runCatching {
if (mode == Mode.FIT_CENTER) {
exoMediaPlayer.setVideoSurface(holder.surface)
} else {
exoMediaPlayer.setVideoSurfaceHolder(holder)
}
val videoUri = this@MovieLiveWallpaperService.let {
AppSharedPreferences.getString(
it,
"absolutePath"
)
}
val isLocalVideo =
AppSharedPreferences.getBool(this@MovieLiveWallpaperService, "isLocalVideo")
when {
isLocalVideo -> exoMediaPlayer.setMediaSource(
buildMediaSourceForLocal(
Uri.parse(
videoUri
)
)
)
!isLocalVideo -> exoMediaPlayer.setMediaSource(
buildMediaSource(
Uri.parse(
videoUri
)
)
)
}
exoMediaPlayer.prepare()
exoMediaPlayer.play()
}
}
private fun buildMediaSource(uri: Uri): MediaSource {
val userAgent = "exoplayer-liveWallpaper"
val media_item: MediaItem = MediaItem.fromUri(uri)
val cache_datasource_factory = CacheDataSource.Factory()
.setCache(EngineVideoCache.getInstance(this@MovieLiveWallpaperService))
.setUpstreamDataSourceFactory(
DefaultHttpDataSource.Factory()
.setUserAgent(userAgent)
)
return ProgressiveMediaSource.Factory(cache_datasource_factory)
.createMediaSource(media_item)
}
private fun buildMediaSourceForLocal(uri: Uri): MediaSource {
val media_item: MediaItem = MediaItem.fromUri(uri)
val dataSource = CacheDataSource.Factory()
.setCache(EngineVideoCache.getInstance(this@MovieLiveWallpaperService))
.setUpstreamDataSourceFactory(DefaultDataSource.Factory(this@MovieLiveWallpaperService))
return ProgressiveMediaSource.Factory(dataSource)
.createMediaSource(media_item)
}
override fun onSurfaceChanged(
holder: SurfaceHolder?,
format: Int,
width: Int,
height: Int
) {
}
@Synchronized
override fun onSurfaceDestroyed(holder: SurfaceHolder?) {
}
override fun onDestroy() {
kotlin.runCatching {
super.onDestroy()
exoMediaPlayer.stop()
exoMediaPlayer.release()
}
}
private fun onPause() {
kotlin.runCatching {
exoMediaPlayer.pause()
}
}
private fun onResume() {
kotlin.runCatching {
exoMediaPlayer.play()
}
}
}
}
Bug Report
- You will email the zip file produced by
adb bugreport
to dev.exoplayer@gmail.com after filing this issue.
Issue Analytics
- State:
- Created a year ago
- Comments:9 (3 by maintainers)
Top Results From Across the Web
Player is accessed on the wrong thread · Issue #8843 - GitHub
One of the purposes of this class is to provide a surface in which a secondary thread can render into the screen. If...
Read more >Exoplayer throws Player is accessed on the wrong thread ...
In onResume() from MainActivity I set exoPlayer.experimentalSetOffloadSchedulingEnabled(false); which throws "Player is accessed on the wrong ...
Read more >Troubleshooting - ExoPlayer
What do “Player is accessed on the wrong thread” errors mean? See A note on threading on the getting started page. How can...
Read more >com.google.android.exoplayer2.SimpleExoPlayer ... - Tabnine
Only * players which are accessed on the main thread are supported ... getApplicationLooper()) { Log.w( TAG, "Player is accessed on the wrong...
Read more >SimpleExoPlayer: Player is accessed on the wrong thread
While watching videos using NewPipe v0.16.2, logcat being spammed with the following line: W SimpleExoPlayer: Player is accessed on the ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
@marcbaechinger This is very much an ExoPlayer issue rather than an app issue, ExoPlayer subscribes to the events from the
SurfaceHolder
inside ofExoPlayerImpl
and theSurfaceHolder
subscribes to theSurfaceView
, the events that are triggered from theSurfaceView
/SurfaceHolder
are always on the main thread (as it’s an Android view). If ExoPlayer is running on a different thread / looper, when the analytics collector then tries to find the media period event time, thegenerateReadingMediaPeriodEventTime
method tries to access the player timeline, which checks the thread and throws anIllegalStateException
.It’s a fairly simple work around in
ExoPlayerImpl
by setting up a handler on theapplicationLooper
and runninglisteners.sendEvent
on that handler inside ofmaybeNotifySurfaceSizeChanged
. To fix it outside of ExoPlayer requires creating a customSurfaceHolder
class that listens to the callbacks and forwards them on the correct thread back to ExoPlayer.I confirm @sixones analysis and can confirm this is reproduced on 2.18.1 release. Here is my callstack, but I don’t think it brings additionnal informations. I tryed to disable analyticsCollector with: setAnalyticsCollector(null) or with player.getAnalyticsCollector().setPlayer(null, null); but unfortunately it doesn’t seem possible.