ExoPlayer踩坑:IndexOutOfBoundsException

项目中语音播放用到Google开源的ExoPlayer,基本使用很简单,但是线上还是出现了一些测试没有发现的crash。其中一个就是IndexOutOfBoundsException。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
com.google.android.exoplayer2.Timeline$1.getPeriod(Timeline.java:516)
2 com.google.android.exoplayer2.Timeline.getPeriod(Timeline.java:749)
3 com.google.android.exoplayer2.ExoPlayerImpl.playbackInfoPositionUsToWindowPositionMs(ExoPlayerImpl.java:689)
4 com.google.android.exoplayer2.ExoPlayerImpl.getCurrentPosition(ExoPlayerImpl.java:474)
5 com.google.android.exoplayer2.ExoPlayerImpl.getContentPosition(ExoPlayerImpl.java:529)
6 com.google.android.exoplayer2.analytics.AnalyticsCollector.generateEventTime(AnalyticsCollector.java:575)
7 com.google.android.exoplayer2.analytics.AnalyticsCollector.generateEventTime(AnalyticsCollector.java:602)
8 com.google.android.exoplayer2.analytics.AnalyticsCollector.generatePlayingMediaPeriodEventTime(AnalyticsCollector.java:612)
9 com.google.android.exoplayer2.analytics.AnalyticsCollector.onTimelineChanged(AnalyticsCollector.java:425)
10 com.google.android.exoplayer2.ExoPlayerImpl$PlaybackInfoUpdate.notifyListeners(ExoPlayerImpl.java:746)
11 com.google.android.exoplayer2.ExoPlayerImpl.updatePlaybackInfo(ExoPlayerImpl.java:681)
12 com.google.android.exoplayer2.ExoPlayerImpl.handlePlaybackInfo(ExoPlayerImpl.java:622)
13 com.google.android.exoplayer2.ExoPlayerImpl.handleEvent(ExoPlayerImpl.java:567)
14 com.google.android.exoplayer2.ExoPlayerImpl$1.handleMessage(ExoPlayerImpl.java:109)
15 android.os.Handler.dispatchMessage(Handler.java:102)
16 android.os.Looper.loop(Looper.java:135)
17 android.app.ActivityThread.main(ActivityThread.java:5318)

错误栈中没有出现app的包名,一脸懵。于是只能google一下,还好发现了问题。在ExoPlayer文档的线程模型中,明确提到了播放器实例必须在一个线程中进行操作。

ExoPlayer instances must be accessed from a single application thread. For the vast majority of cases this should be the application’s main thread. Using the application’s main thread is also a requirement when using ExoPlayer’s UI components or the IMA extension. The thread on which an ExoPlayer instance must be accessed can be explicitly specified by passing a Looper when creating the player. If no Looper is specified, then the Looper of the thread that the player is created on is used, or if that thread does not have a Looper, the Looper of the application’s main thread is used. In all cases the Looper of the thread from which the player must be accessed can be queried using Player.getApplicationLooper().

参考:
https://github.com/google/ExoPlayer/issues/4278#issue-325140509
https://github.com/google/ExoPlayer/commit/cd65cc85e21a1c890760fab132c4e4d8a8b2f7da
http://google.github.io/ExoPlayer/guide.html
https://github.com/google/ExoPlayer/issues/4641