A wired issue of MediaPlayer on Android

Background

Our application needs to play mp3 files served by our server. What we used to do is just using MediaPlayer to play that URL. However, there are more and more customers complaint about Music playing being cut off quite frequently. So I had to dig into legacy codes to find out the reason.

Reproduce

From existing codebase, I could tell our app respects MediaPlayer's OnErrorListener and if MediaPlay throw out any Error, app will try to catch them and recover. Unfortunately, we did not receive any exceptions for this part according to our Fabric crashlytics report. Besides that, this bug is not really easy to reproduce. 
I knew it must be caused by user's network issue. So I tried Charles proxy and using its throttling feature. However it seems like MediaPlayer would have a special connection after connecting and Charles' throttling can not control that connection.
Then I also tried Android Emulator's build-in network status simulation feature. Still, it is not easy to reproduce that with Poor-GPRS. MediaPlayer either can not connect at all or without any playing issue after connecting successfully.
So I tried another hacky way. After MediaPlayer start playing, I turned off my network manually. Finally, I could reproduce this issue easily. 

Problems

W/MediaPlayer: info/warning (703, 0)
W/MediaPlayer: info/warning (701, 0)
W/MediaHTTPConnection: readAt 1711126 / 32768 => java.net.ProtocolException: unexpected end of stream
I found this warning from Logcat but OnErrorListener was not being triggered. From the warning log, I could tell the connection was totally disconnected(Of course). Then I found another interesting thing, OnCompletionListeneractually was being triggered. Which explains our app's wired behaviour because our app considers music playing completed succssfully.

Solution

Luckily, in OnCompletionListener, we could still get current position and we know our music file's total length. So how we try to fix this issue is that, in OnCompletionListener, if current playing position has not reached the end of the music file, app would consider there is something wrong due to network issue. Then app will try to recreate MusicPlayer and seekTo that last position after data source is ready.

Summary

  • MusicPlayer has its own reconnect/rety implementation. Mostly, it will recover by itself.
  • MusicPlayer will trigger OnCompletionListener instead of OnErrorListener if there is disconnection happened.
  • From the source codes, MusicPlayer's timeout is 30 seconds by default.
  • MusicPlayer's getDuration() would cause error if MusicPlayer's state is not ready.
  • The only way to recover from such disconnection is recreating MusicPlayer and seek its position to previous one.
  • We will replace our MediaPlayer with ExoPlayer2 and hopefully we could leverage with OkHttpClient

Comments

Popular posts from this blog

How to add pre-receive hook in GitLab

Problem when using Jackson json parser in Android.