MediaPlayer 播放網(wǎng)絡音頻踩坑記錄

??MediaPlayer 錯誤碼

??在最近項目中使用MediaPlayer發(fā)現(xiàn)在業(yè)務邏輯復雜一些的情況下身隐,使用Mediaplayer頻繁重復播放音頻的情況下極其容易崩潰,并且出現(xiàn)了無法播放網(wǎng)絡音頻資源的情況稽揭,但是回想之前其他項目中使用MediaPlayer播放網(wǎng)絡短音視頻資源沒有出現(xiàn)過這種問題党巾,感到十分困惑,于是決定重新開始一探究竟臂聋。

??需要解決兩個問題:

  1. MediaPlayer API安全調用光稼,避免崩潰;
  2. 網(wǎng)絡音頻無法播放問題孩等;

??官方建議在使用時候要捕獲異常艾君,但是在考慮了Mediaplayer整體生命周期之后蓝角,MediaPlayer的API必須要在相應的狀態(tài)下才能調用亭枷,調用后會產(chǎn)生新的狀態(tài),因此更好的方案應該通過記錄當前播放狀態(tài)來達到安全調用的目的避免崩潰捻勉,而不是放任異常然后去捕獲权她,但是捕獲異常仍然可以作為輔助措施以防應用崩潰虹茶。另外在MediaPlayer生命周期內API調用異常的情況下也會導致播放器不斷重試加載資源而無法播放的情況,需要周詳考慮伴奥。

??網(wǎng)上已經(jīng)有比較好的生命周期的播放官方資料的翻譯写烤,我就不多贅言。
?? MediaPlayer 狀態(tài)機 API 詳解 示例
?? Android Developer MediaPlayer

??在實現(xiàn)過程中發(fā)現(xiàn)MediaPlayer一直無法播放網(wǎng)絡音頻地址http://music.163.com/song/media/outer/url?id=317151.mp3拾徙,并且輸出如下log:

2019-07-20 18:10:42.194 29460-29460/com.khronos.issue I/MediaPlayer: constructor
2019-07-20 18:10:42.206 29460-29460/com.khronos.issue W/MediaPlayer: Use of stream types is deprecated for operations other than volume control
2019-07-20 18:10:42.206 29460-29460/com.khronos.issue W/MediaPlayer: See the documentation of setAudioStreamType() for what to use instead with android.media.AudioAttributes to qualify your playback use case
2019-07-20 18:10:42.216 29460-29460/com.khronos.issue V/MediaPlayer: resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
2019-07-20 18:10:42.216 29460-29460/com.khronos.issue V/MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
2019-07-20 18:10:42.218 29460-29460/com.khronos.issue I/MediaPlayer: constructor
2019-07-20 18:10:42.224 29460-29460/com.khronos.issue W/MediaPlayer: Use of stream types is deprecated for operations other than volume control
2019-07-20 18:10:42.224 29460-29460/com.khronos.issue W/MediaPlayer: See the documentation of setAudioStreamType() for what to use instead with android.media.AudioAttributes to qualify your playback use case
2019-07-20 18:10:42.228 29460-29460/com.khronos.issue W/m.khronos.issu: Accessing hidden method Landroid/media/MediaPlayer;->setDataSource(Ljava/lang/String;Ljava/util/Map;)V (light greylist, reflection)
2019-07-20 18:10:42.228 29460-29460/com.khronos.issue I/MediaPlayer: setDataSource:http://music.163.com/song/media/outer/url?id=317151.mp3
2019-07-20 18:10:42.228 29460-29460/com.khronos.issue V/MediaHTTPService: MediaHTTPService(android.media.MediaHTTPService@aa4f989): Cookies: null
2019-07-20 18:10:42.230 932-18904/? D/MediaPlayerService: LF player type = 4
2019-07-20 18:10:42.231 932-18904/? D/NuPlayerDriver: NuPlayerDriver(0xe5921c00) created, clientPid(29460)
2019-07-20 18:10:42.235 29460-29540/com.khronos.issue V/MediaHTTPService: makeHTTPConnection: CookieManager created: java.net.CookieManager@53c2d8e
2019-07-20 18:10:42.235 29460-29540/com.khronos.issue V/MediaHTTPService: makeHTTPConnection(android.media.MediaHTTPService@aa4f989): cookieHandler: java.net.CookieManager@53c2d8e Cookies: null
2019-07-20 18:10:42.241 29460-29540/com.khronos.issue I/DpmTcmClient: RegisterTcmMonitor from: com.android.okhttp.TcmIdleTimerMonitor
2019-07-20 18:10:42.249 2894-2908/? V/OPConfig:ConfigProvider: Module:GameModeImComponents
2019-07-20 18:10:42.250 29460-29540/com.khronos.issue D/NetworkSecurityConfig: Using Network Security Config from resource xms_network_security_config debugBuild: true
2019-07-20 18:10:42.261 932-29605/? E/NuCachedSource2: source returned error -1, 10 retries left
2019-07-20 18:10:42.267 29460-29603/com.khronos.issue E/Config:Grabber:GameModeImComponents: Exception:Value de53f99085b83be13e7d7ae3c37d9528177b49f73c95e86886404ffe9b8c0855cfbdfcfc85713ea2cdf2e7aa44b0c5d2b15e362dfc78808a11ddb82d823c928e5bf5270d46222c74ee528e5c19b72c0746b7f31bf949bec78c03edd7da2975d623e552f35f5a36f80c93cd2e11337c2c82c8eaec42f4a5304fe97742db7739f3ec1a7afe40bab7e3d879f2d7989a153a3eaef8078a4a0aa80182629b887e6ced525cff4910cbc27572f536f23274eaf112df11d5fb23fb79a0e30e3ceefbd8a803e2c30743d02d869a2a2b64ac9ea0304321299381029bc541f6e7c251b08b339000ee2695eb6441ca5a11069442b4ca38b1c970b907b59cc8cc307218c80ff88a1289a56fe9b934b6157e82a018bd1f1abafa66f24f07181db49eff9dde5ec3905d241e714bb2adfdde1dfbe60e9a3dacebccedb3810fc79d7adff30964a10adcf0305cbfa598da92b6d866f955479d010c99808a66f9c550b80ef7cdb23c9d212fc1321fa5ded0916f3e29ca549fc5fa4f7b49c5dfe62ea2af0e3e9ba61ecfd84a85886747941cc18bd1994e12afbf1db2be1547ee43f5a29efdc18b4c9f27d66b55a8c19eef7f876e95d1020d89dabcf0316c4662e363e10430a905455f76032a32c01043fd60af08203d605ec55e03eb8fee26c533fee7a93f05dbce244c5aa975cb666a633532654549a6685941d39211e70d8c35e12a993c3a4ede634eba805ae47c972fd921f55ccad345a1e878ac37bf37e031c3cdc5d02618a8d9c99ac23b204ee7212ad09df11f835c4dcf10c36d2a76ae0bfce5b1715bd4191f7a8a819817311f53b9ac415839503a9e56137d2a0f4eabe1ab482d9863768da460cb1ad19c22a66174e1119f06358de4303a6f3ab734235b27ff6070cc1a059820a770cfa7f289f8565e734222b5d9673cd606a6012b88bd26274ef3479f4606a411589a190421e4f2af2889d69c2a047a64d7168b1b0f645753b43b5cee97b3bc05b0da5926f00d57b6dba6334bb56dd28ca0d6837d024b40d4d52c2f3932a148978f12f93cd590f86e171fb1e3ca6c478559709c0a33759c9aae4eff65bd398f99200f108cc64a9445fafe4218fe6ff6196a66820ac6a37906f063476aa983ea62abf5a64c8f01e8a9a6ce44b3f4086156a22bf4258c616eacfe97faa25801dec717321be07e43c341fbbddce894ce50f5de06b1d2cc5202ac8f581e64eafaea4b980a67acda79ebdaa52fc9e8230ee3c94b7e45bb9e33bb7252dfb9289779f5eef7477ac80ed948f2a34e661196fc692be434dcfb854adac5304016b0c97cf23f324e4399be706d015682f5ecf2f66b45dff468d425b81cb1a346d01a8f606abfc632ad4e6035c26dc09ef9002f64ba071c9c8dc4fbb8735b8665c0e43510024e2cdeff8b50b3bda3773967365f97314a9a1c78f19c3c71e1c84138a704539264419e8a8d9f1c3c2c620f870dd497c36981dae8983397a36399d1b1805263df98506e4e35a96b751c28de839140f0d26e58356cc3203d322a1fc0fc3b47e9deca38da35774cb1364ea7d7800282dbb87cde710ed88aeb3022678c96ce346ce0327a73c35343bb3e890de0bb968e10496b7201eb3e624c74904770af0128752209890c5bba782770b9773498a02d5517429a50f31344998e028583029402b3480fb0f96121c777f180ff35e3d945ec94c6c6c5af8960495a2b71266413a82c756991f7f3dd2a8234f909d9f8a93f4eeb919606e0983a68953a144d97caa42a23b9dfe84bd52c351d262076dec15d5bdf of type java.lang.String cannot be converted to JSONArray
2019-07-20 18:10:42.269 2894-2908/? V/OPConfig:ConfigProvider: Module:GameModeImComponents
2019-07-20 18:10:42.278 29460-29606/com.khronos.issue E/Config:Grabber:GameModeImComponents: Exception:Value de53f99085b83be13e7d7ae3c37d9528177b49f73c95e86886404ffe9b8c0855cfbdfcfc85713ea2cdf2e7aa44b0c5d2b15e362dfc78808a11ddb82d823c928e5bf5270d46222c74ee528e5c19b72c0746b7f31bf949bec78c03edd7da2975d623e552f35f5a36f80c93cd2e11337c2c82c8eaec42f4a5304fe97742db7739f3ec1a7afe40bab7e3d879f2d7989a153a3eaef8078a4a0aa80182629b887e6ced525cff4910cbc27572f536f23274eaf112df11d5fb23fb79a0e30e3ceefbd8a803e2c30743d02d869a2a2b64ac9ea0304321299381029bc541f6e7c251b08b339000ee2695eb6441ca5a11069442b4ca38b1c970b907b59cc8cc307218c80ff88a1289a56fe9b934b6157e82a018bd1f1abafa66f24f07181db49eff9dde5ec3905d241e714bb2adfdde1dfbe60e9a3dacebccedb3810fc79d7adff30964a10adcf0305cbfa598da92b6d866f955479d010c99808a66f9c550b80ef7cdb23c9d212fc1321fa5ded0916f3e29ca549fc5fa4f7b49c5dfe62ea2af0e3e9ba61ecfd84a85886747941cc18bd1994e12afbf1db2be1547ee43f5a29efdc18b4c9f27d66b55a8c19eef7f876e95d1020d89dabcf0316c4662e363e10430a905455f76032a32c01043fd60af08203d605ec55e03eb8fee26c533fee7a93f05dbce244c5aa975cb666a633532654549a6685941d39211e70d8c35e12a993c3a4ede634eba805ae47c972fd921f55ccad345a1e878ac37bf37e031c3cdc5d02618a8d9c99ac23b204ee7212ad09df11f835c4dcf10c36d2a76ae0bfce5b1715bd4191f7a8a819817311f53b9ac415839503a9e56137d2a0f4eabe1ab482d9863768da460cb1ad19c22a66174e1119f06358de4303a6f3ab734235b27ff6070cc1a059820a770cfa7f289f8565e734222b5d9673cd606a6012b88bd26274ef3479f4606a411589a190421e4f2af2889d69c2a047a64d7168b1b0f645753b43b5cee97b3bc05b0da5926f00d57b6dba6334bb56dd28ca0d6837d024b40d4d52c2f3932a148978f12f93cd590f86e171fb1e3ca6c478559709c0a33759c9aae4eff65bd398f99200f108cc64a9445fafe4218fe6ff6196a66820ac6a37906f063476aa983ea62abf5a64c8f01e8a9a6ce44b3f4086156a22bf4258c616eacfe97faa25801dec717321be07e43c341fbbddce894ce50f5de06b1d2cc5202ac8f581e64eafaea4b980a67acda79ebdaa52fc9e8230ee3c94b7e45bb9e33bb7252dfb9289779f5eef7477ac80ed948f2a34e661196fc692be434dcfb854adac5304016b0c97cf23f324e4399be706d015682f5ecf2f66b45dff468d425b81cb1a346d01a8f606abfc632ad4e6035c26dc09ef9002f64ba071c9c8dc4fbb8735b8665c0e43510024e2cdeff8b50b3bda3773967365f97314a9a1c78f19c3c71e1c84138a704539264419e8a8d9f1c3c2c620f870dd497c36981dae8983397a36399d1b1805263df98506e4e35a96b751c28de839140f0d26e58356cc3203d322a1fc0fc3b47e9deca38da35774cb1364ea7d7800282dbb87cde710ed88aeb3022678c96ce346ce0327a73c35343bb3e890de0bb968e10496b7201eb3e624c74904770af0128752209890c5bba782770b9773498a02d5517429a50f31344998e028583029402b3480fb0f96121c777f180ff35e3d945ec94c6c6c5af8960495a2b71266413a82c756991f7f3dd2a8234f909d9f8a93f4eeb919606e0983a68953a144d97caa42a23b9dfe84bd52c351d262076dec15d5bdf of type java.lang.String cannot be converted to JSONArray
2019-07-20 18:10:42.279 2894-2908/? V/OPConfig:ConfigProvider: Module:GameModeImComponents
2019-07-20 18:10:42.288 29460-29607/com.khronos.issue E/Config:Grabber:GameModeImComponents: Exception:Value de53f99085b83be13e7d7ae3c37d9528177b49f73c95e86886404ffe9b8c0855cfbdfcfc85713ea2cdf2e7aa44b0c5d2b15e362dfc78808a11ddb82d823c928e5bf5270d46222c74ee528e5c19b72c0746b7f31bf949bec78c03edd7da2975d623e552f35f5a36f80c93cd2e11337c2c82c8eaec42f4a5304fe97742db7739f3ec1a7afe40bab7e3d879f2d7989a153a3eaef8078a4a0aa80182629b887e6ced525cff4910cbc27572f536f23274eaf112df11d5fb23fb79a0e30e3ceefbd8a803e2c30743d02d869a2a2b64ac9ea0304321299381029bc541f6e7c251b08b339000ee2695eb6441ca5a11069442b4ca38b1c970b907b59cc8cc307218c80ff88a1289a56fe9b934b6157e82a018bd1f1abafa66f24f07181db49eff9dde5ec3905d241e714bb2adfdde1dfbe60e9a3dacebccedb3810fc79d7adff30964a10adcf0305cbfa598da92b6d866f955479d010c99808a66f9c550b80ef7cdb23c9d212fc1321fa5ded0916f3e29ca549fc5fa4f7b49c5dfe62ea2af0e3e9ba61ecfd84a85886747941cc18bd1994e12afbf1db2be1547ee43f5a29efdc18b4c9f27d66b55a8c19eef7f876e95d1020d89dabcf0316c4662e363e10430a905455f76032a32c01043fd60af08203d605ec55e03eb8fee26c533fee7a93f05dbce244c5aa975cb666a633532654549a6685941d39211e70d8c35e12a993c3a4ede634eba805ae47c972fd921f55ccad345a1e878ac37bf37e031c3cdc5d02618a8d9c99ac23b204ee7212ad09df11f835c4dcf10c36d2a76ae0bfce5b1715bd4191f7a8a819817311f53b9ac415839503a9e56137d2a0f4eabe1ab482d9863768da460cb1ad19c22a66174e1119f06358de4303a6f3ab734235b27ff6070cc1a059820a770cfa7f289f8565e734222b5d9673cd606a6012b88bd26274ef3479f4606a411589a190421e4f2af2889d69c2a047a64d7168b1b0f645753b43b5cee97b3bc05b0da5926f00d57b6dba6334bb56dd28ca0d6837d024b40d4d52c2f3932a148978f12f93cd590f86e171fb1e3ca6c478559709c0a33759c9aae4eff65bd398f99200f108cc64a9445fafe4218fe6ff6196a66820ac6a37906f063476aa983ea62abf5a64c8f01e8a9a6ce44b3f4086156a22bf4258c616eacfe97faa25801dec717321be07e43c341fbbddce894ce50f5de06b1d2cc5202ac8f581e64eafaea4b980a67acda79ebdaa52fc9e8230ee3c94b7e45bb9e33bb7252dfb9289779f5eef7477ac80ed948f2a34e661196fc692be434dcfb854adac5304016b0c97cf23f324e4399be706d015682f5ecf2f66b45dff468d425b81cb1a346d01a8f606abfc632ad4e6035c26dc09ef9002f64ba071c9c8dc4fbb8735b8665c0e43510024e2cdeff8b50b3bda3773967365f97314a9a1c78f19c3c71e1c84138a704539264419e8a8d9f1c3c2c620f870dd497c36981dae8983397a36399d1b1805263df98506e4e35a96b751c28de839140f0d26e58356cc3203d322a1fc0fc3b47e9deca38da35774cb1364ea7d7800282dbb87cde710ed88aeb3022678c96ce346ce0327a73c35343bb3e890de0bb968e10496b7201eb3e624c74904770af0128752209890c5bba782770b9773498a02d5517429a50f31344998e028583029402b3480fb0f96121c777f180ff35e3d945ec94c6c6c5af8960495a2b71266413a82c756991f7f3dd2a8234f909d9f8a93f4eeb919606e0983a68953a144d97caa42a23b9dfe84bd52c351d262076dec15d5bdf of type java.lang.String cannot be converted to JSONArray
2019-07-20 18:10:42.293 2894-2908/? V/OPConfig:ConfigProvider: Module:GameModeImComponents
2019-07-20 18:10:42.302 29460-29608/com.khronos.issue E/Config:Grabber:GameModeImComponents: Exception:Value de53f99085b83be13e7d7ae3c37d9528177b49f73c95e86886404ffe9b8c0855cfbdfcfc85713ea2cdf2e7aa44b0c5d2b15e362dfc78808a11ddb82d823c928e5bf5270d46222c74ee528e5c19b72c0746b7f31bf949bec78c03edd7da2975d623e552f35f5a36f80c93cd2e11337c2c82c8eaec42f4a5304fe97742db7739f3ec1a7afe40bab7e3d879f2d7989a153a3eaef8078a4a0aa80182629b887e6ced525cff4910cbc27572f536f23274eaf112df11d5fb23fb79a0e30e3ceefbd8a803e2c30743d02d869a2a2b64ac9ea0304321299381029bc541f6e7c251b08b339000ee2695eb6441ca5a11069442b4ca38b1c970b907b59cc8cc307218c80ff88a1289a56fe9b934b6157e82a018bd1f1abafa66f24f07181db49eff9dde5ec3905d241e714bb2adfdde1dfbe60e9a3dacebccedb3810fc79d7adff30964a10adcf0305cbfa598da92b6d866f955479d010c99808a66f9c550b80ef7cdb23c9d212fc1321fa5ded0916f3e29ca549fc5fa4f7b49c5dfe62ea2af0e3e9ba61ecfd84a85886747941cc18bd1994e12afbf1db2be1547ee43f5a29efdc18b4c9f27d66b55a8c19eef7f876e95d1020d89dabcf0316c4662e363e10430a905455f76032a32c01043fd60af08203d605ec55e03eb8fee26c533fee7a93f05dbce244c5aa975cb666a633532654549a6685941d39211e70d8c35e12a993c3a4ede634eba805ae47c972fd921f55ccad345a1e878ac37bf37e031c3cdc5d02618a8d9c99ac23b204ee7212ad09df11f835c4dcf10c36d2a76ae0bfce5b1715bd4191f7a8a819817311f53b9ac415839503a9e56137d2a0f4eabe1ab482d9863768da460cb1ad19c22a66174e1119f06358de4303a6f3ab734235b27ff6070cc1a059820a770cfa7f289f8565e734222b5d9673cd606a6012b88bd26274ef3479f4606a411589a190421e4f2af2889d69c2a047a64d7168b1b0f645753b43b5cee97b3bc05b0da5926f00d57b6dba6334bb56dd28ca0d6837d024b40d4d52c2f3932a148978f12f93cd590f86e171fb1e3ca6c478559709c0a33759c9aae4eff65bd398f99200f108cc64a9445fafe4218fe6ff6196a66820ac6a37906f063476aa983ea62abf5a64c8f01e8a9a6ce44b3f4086156a22bf4258c616eacfe97faa25801dec717321be07e43c341fbbddce894ce50f5de06b1d2cc5202ac8f581e64eafaea4b980a67acda79ebdaa52fc9e8230ee3c94b7e45bb9e33bb7252dfb9289779f5eef7477ac80ed948f2a34e661196fc692be434dcfb854adac5304016b0c97cf23f324e4399be706d015682f5ecf2f66b45dff468d425b81cb1a346d01a8f606abfc632ad4e6035c26dc09ef9002f64ba071c9c8dc4fbb8735b8665c0e43510024e2cdeff8b50b3bda3773967365f97314a9a1c78f19c3c71e1c84138a704539264419e8a8d9f1c3c2c620f870dd497c36981dae8983397a36399d1b1805263df98506e4e35a96b751c28de839140f0d26e58356cc3203d322a1fc0fc3b47e9deca38da35774cb1364ea7d7800282dbb87cde710ed88aeb3022678c96ce346ce0327a73c35343bb3e890de0bb968e10496b7201eb3e624c74904770af0128752209890c5bba782770b9773498a02d5517429a50f31344998e028583029402b3480fb0f96121c777f180ff35e3d945ec94c6c6c5af8960495a2b71266413a82c756991f7f3dd2a8234f909d9f8a93f4eeb919606e0983a68953a144d97caa42a23b9dfe84bd52c351d262076dec15d5bdf of type java.lang.String cannot be converted to JSONArray
    
    --------- beginning of system
2019-07-20 18:10:43.894 1318-2336/? V/AlarmManager: Triggering alarm #0: 2 when =75078454 package =android operation = null listenTag =*job.delay* flags =0x0
2019-07-20 18:10:43.911 29547-29547/com.khronos.issue.MyJobService E/Job:  onStartJob com.khronos.issue.job.schedule.MyJobService@3a91916
2019-07-20 18:10:43.916 29519-29519/com.khronos.issue:nfls E/Job:  onStartCommand com.khronos.issue.job.KeepAliveService@3a91916
2019-07-20 18:10:45.162 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:10:45.287 932-29605/? E/NuCachedSource2: source returned error -1, 9 retries left
2019-07-20 18:10:46.061 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:46.063 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:46.792 2482-2595/? D/NetworkController.MobileSignalController(1): onSignalStrengthsChanged signalStrength=SignalStrength: 99 0 -120 -160 -120 -160 -1 24 -96 -11 138 2147483647 0 2147483647 4 4 99 255 2147483647 gsm|lte use_rsrp_and_rssnr_for_lte_level  [-128, -118, -108, -98] [-115, -105, -95, -85] level=4 voicelevel=4 datalevel=4
2019-07-20 18:10:46.853 2482-2482/? E/ndroid.systemu: Invalid ID 0x00000000.
2019-07-20 18:10:48.312 932-29605/? E/NuCachedSource2: source returned error -1, 8 retries left
2019-07-20 18:10:50.220 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:10:51.063 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:51.064 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:51.322 932-29605/? E/NuCachedSource2: source returned error -1, 7 retries left
2019-07-20 18:10:53.696 1318-2336/? V/AlarmManager: Triggering alarm #0: 2 when =75088254 package =com.eg.android.AlipayGphone operation =*walarm*:ALARM_ACTION(3932) flags =0x1
2019-07-20 18:10:53.730 1318-1377/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.net.conn.DATA_ACTIVITY_CHANGE flg=0x10 (has extras) } to com.oneplus.security/.network.trafficalarm.TrafficUsageAlarmReceiver
2019-07-20 18:10:54.341 932-29605/? E/NuCachedSource2: source returned error -1, 6 retries left
2019-07-20 18:10:55.270 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:10:56.064 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:56.063 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:57.349 932-29605/? E/NuCachedSource2: source returned error -1, 5 retries left
2019-07-20 18:11:00.001 1318-2336/? V/AlarmManager: Triggering alarm #0: 3 when =75094560 package =android operation =*alarm*:android.intent.action.TIME_TICK flags =0x1
2019-07-20 18:11:00.017 3084-3084/? I/DigitalWidgetViewsFactory: getCount() return 0 !!!
2019-07-20 18:11:00.057 2482-2575/? I/ClockCtrl:  schedule next: 59943
2019-07-20 18:11:00.057 2482-2575/? I/KeyguardUpdateMonitor: onTimeChanged
2019-07-20 18:11:00.058 2482-2482/? D/KeyguardUpdateMonitor: handleTimeUpdate
2019-07-20 18:11:00.062 2482-2482/? I/Clock: onTimeChanged
2019-07-20 18:11:00.062 2482-2482/? I/Clock: onTimeChanged
2019-07-20 18:11:00.062 2482-2482/? I/DateView: onTimeChanged
2019-07-20 18:11:00.322 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:00.368 932-29605/? E/NuCachedSource2: source returned error -1, 4 retries left
2019-07-20 18:11:01.065 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:01.065 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:03.384 932-29605/? E/NuCachedSource2: source returned error -1, 3 retries left
2019-07-20 18:11:04.019 1318-1377/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.net.conn.DATA_ACTIVITY_CHANGE flg=0x10 (has extras) } to com.oneplus.security/.network.trafficalarm.TrafficUsageAlarmReceiver
2019-07-20 18:11:05.380 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:06.066 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:06.067 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:06.409 932-29605/? E/NuCachedSource2: source returned error -1, 2 retries left
2019-07-20 18:11:09.425 932-29605/? E/NuCachedSource2: source returned error -1, 1 retries left
2019-07-20 18:11:10.423 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:11.070 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:11.071 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:11.588 1318-1377/? D/ActivityManager: skip: com.tencent.mm
2019-07-20 18:11:11.590 797-797/? D/AudioPolicyService: setRecordSilenced() uid 10021 on silenced 1
2019-07-20 18:11:11.605 797-797/? D/AudioPolicyService: setRecordSilenced() uid 10069 on silenced 1
2019-07-20 18:11:12.442 932-29605/? E/NuCachedSource2: source returned error -1, 0 retries left
2019-07-20 18:11:12.501 932-29602/? E/GenericSource: initFromDataSource, cannot create extractor!
2019-07-20 18:11:12.501 932-29602/? E/GenericSource: Failed to init from data source!
2019-07-20 18:11:12.501 932-29601/? D/NuPlayerDriver: notifyListener_l(0xe5921c00), (100, 1, -2147483648, -1), loop setting(0, 0)
2019-07-20 18:11:12.502 29460-29476/com.khronos.issue E/MediaPlayerNative: error (1, -2147483648)
2019-07-20 18:11:12.503 29460-29460/com.khronos.issue E/MediaPlayer: Error (1,-2147483648)
2019-07-20 18:11:12.506 29460-29460/com.khronos.issue V/MediaPlayer: resetDrmState:  mDrmInfo=null mDrmProvisioningThread=null mPrepareDrmInProgress=false mActiveDrmScheme=false
2019-07-20 18:11:12.506 29460-29460/com.khronos.issue V/MediaPlayer: cleanDrmObj: mDrmObj=null mDrmSessionId=null
2019-07-20 18:11:12.506 932-18904/? D/NuPlayerDriver: reset(0xe5921c00) at state 2
2019-07-20 18:11:12.510 932-18904/? D/NuPlayerDriver: notifyListener_l(0xe5921c00), (8, 0, 0, -1), loop setting(0, 0)
2019-07-20 18:11:12.510 932-29601/? W/AMessage: failed to post message as target looper for handler 0 is gone.
2019-07-20 18:11:12.511 932-29601/? D/NuPlayerDriver: notifyResetComplete(0xe5921c00)
2019-07-20 18:11:12.513 29460-29460/com.khronos.issue E/Khronos-MediaPlayerManager: ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2019-07-20 18:11:12.513 29460-29460/com.khronos.issue E/Khronos-MediaPlayerManager: │ Thread: main
2019-07-20 18:11:12.513 29460-29460/com.khronos.issue E/Khronos-MediaPlayerManager: ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2019-07-20 18:11:12.514 29460-29460/com.khronos.issue E/Khronos-MediaPlayerManager: │ onError:mp = android.media.MediaPlayer@8c04fa7, what = 1, extra = -2147483648
2019-07-20 18:11:12.514 29460-29460/com.khronos.issue E/Khronos-MediaPlayerManager: └────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2019-07-20 18:11:11.604 1318-1377/? I/chatty: uid=1000(system) ActivityManager identical 2 lines

在上述日志中有一段如下:

2019-07-20 18:10:42.228 29460-29460/com.khronos.issue I/MediaPlayer: setDataSource:http://music.163.com/song/media/outer/url?id=317151.mp3
2019-07-20 18:10:42.228 29460-29460/com.khronos.issue V/MediaHTTPService: MediaHTTPService(android.media.MediaHTTPService@aa4f989): Cookies: null
2019-07-20 18:10:42.230 932-18904/? D/MediaPlayerService: LF player type = 4
2019-07-20 18:10:42.231 932-18904/? D/NuPlayerDriver: NuPlayerDriver(0xe5921c00) created, clientPid(29460)
2019-07-20 18:10:42.235 29460-29540/com.khronos.issue V/MediaHTTPService: makeHTTPConnection: CookieManager created: java.net.CookieManager@53c2d8e
2019-07-20 18:10:42.235 29460-29540/com.khronos.issue V/MediaHTTPService: makeHTTPConnection(android.media.MediaHTTPService@aa4f989): cookieHandler: java.net.CookieManager@53c2d8e Cookies: null
2019-07-20 18:10:42.241 29460-29540/com.khronos.issue I/DpmTcmClient: RegisterTcmMonitor from: com.android.okhttp.TcmIdleTimerMonitor
2019-07-20 18:10:42.249 2894-2908/? V/OPConfig:ConfigProvider: Module:GameModeImComponents
2019-07-20 18:10:42.250 29460-29540/com.khronos.issue D/NetworkSecurityConfig: Using Network Security Config from resource xms_network_security_config debugBuild: true

日志中顯示執(zhí)行網(wǎng)絡請求,并且使用了項目中的網(wǎng)絡配置文件感局,并且執(zhí)行了9次的資源加載重試

2019-07-20 18:10:45.287 932-29605/? E/NuCachedSource2: source returned error -1, 9 retries left
2019-07-20 18:10:46.061 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:46.063 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:46.792 2482-2595/? D/NetworkController.MobileSignalController(1): onSignalStrengthsChanged signalStrength=SignalStrength: 99 0 -120 -160 -120 -160 -1 24 -96 -11 138 2147483647 0 2147483647 4 4 99 255 2147483647 gsm|lte use_rsrp_and_rssnr_for_lte_level  [-128, -118, -108, -98] [-115, -105, -95, -85] level=4 voicelevel=4 datalevel=4
2019-07-20 18:10:46.853 2482-2482/? E/ndroid.systemu: Invalid ID 0x00000000.
2019-07-20 18:10:48.312 932-29605/? E/NuCachedSource2: source returned error -1, 8 retries left
2019-07-20 18:10:50.220 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:10:51.063 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:51.064 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:51.322 932-29605/? E/NuCachedSource2: source returned error -1, 7 retries left
2019-07-20 18:10:53.696 1318-2336/? V/AlarmManager: Triggering alarm #0: 2 when =75088254 package =com.eg.android.AlipayGphone operation =*walarm*:ALARM_ACTION(3932) flags =0x1
2019-07-20 18:10:53.730 1318-1377/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.net.conn.DATA_ACTIVITY_CHANGE flg=0x10 (has extras) } to com.oneplus.security/.network.trafficalarm.TrafficUsageAlarmReceiver
2019-07-20 18:10:54.341 932-29605/? E/NuCachedSource2: source returned error -1, 6 retries left
2019-07-20 18:10:55.270 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:10:56.064 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:10:56.063 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:10:57.349 932-29605/? E/NuCachedSource2: source returned error -1, 5 retries left
2019-07-20 18:11:00.001 1318-2336/? V/AlarmManager: Triggering alarm #0: 3 when =75094560 package =android operation =*alarm*:android.intent.action.TIME_TICK flags =0x1
2019-07-20 18:11:00.017 3084-3084/? I/DigitalWidgetViewsFactory: getCount() return 0 !!!
2019-07-20 18:11:00.057 2482-2575/? I/ClockCtrl:  schedule next: 59943
2019-07-20 18:11:00.057 2482-2575/? I/KeyguardUpdateMonitor: onTimeChanged
2019-07-20 18:11:00.058 2482-2482/? D/KeyguardUpdateMonitor: handleTimeUpdate
2019-07-20 18:11:00.062 2482-2482/? I/Clock: onTimeChanged
2019-07-20 18:11:00.062 2482-2482/? I/Clock: onTimeChanged
2019-07-20 18:11:00.062 2482-2482/? I/DateView: onTimeChanged
2019-07-20 18:11:00.322 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:00.368 932-29605/? E/NuCachedSource2: source returned error -1, 4 retries left
2019-07-20 18:11:01.065 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:01.065 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:03.384 932-29605/? E/NuCachedSource2: source returned error -1, 3 retries left
2019-07-20 18:11:04.019 1318-1377/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.net.conn.DATA_ACTIVITY_CHANGE flg=0x10 (has extras) } to com.oneplus.security/.network.trafficalarm.TrafficUsageAlarmReceiver
2019-07-20 18:11:05.380 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:06.066 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:06.067 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:06.409 932-29605/? E/NuCachedSource2: source returned error -1, 2 retries left
2019-07-20 18:11:09.425 932-29605/? E/NuCachedSource2: source returned error -1, 1 retries left
2019-07-20 18:11:10.423 611-611/? W/auditd: type=1400 "CokaNew-4""loadavg""proc"
2019-07-20 18:11:11.070 917-1046/? E/libc: Access denied finding property "sys.thermal.para"
2019-07-20 18:11:11.071 611-611/? W/auditd: type=1400 "thermal-engine""u:object_r:system_prop:s0""tmpfs"
2019-07-20 18:11:11.588 1318-1377/? D/ActivityManager: skip: com.tencent.mm
2019-07-20 18:11:11.590 797-797/? D/AudioPolicyService: setRecordSilenced() uid 10021 on silenced 1
2019-07-20 18:11:11.605 797-797/? D/AudioPolicyService: setRecordSilenced() uid 10069 on silenced 1
2019-07-20 18:11:12.442 932-29605/? E/NuCachedSource2: source returned error -1, 0 retries left

最終加載失敗

2019-07-20 18:11:12.501 932-29602/? E/GenericSource: initFromDataSource, cannot create extractor!
2019-07-20 18:11:12.501 932-29602/? E/GenericSource: Failed to init from data source!

??通過google以上日志查找到Android MediaPlayer使用之網(wǎng)絡訪問異常尼啡,頓時驚醒!Qⅰ崖瞭!我使用的網(wǎng)絡音頻地址是http地址,而我的手機系統(tǒng)是Android 9.03琶书聚!關于Android 9.0 關于http明文設置源碼解析相關可以查看源碼分析 Android 9.0 http請求適配原理。之前公司的測試曾提出無法在Android 7.0系統(tǒng)上使用Charles抓包,后通過官方資料網(wǎng)絡安全配置發(fā)現(xiàn)在Android7.0系統(tǒng)上默認已經(jīng)不再信任用戶證書雌续,按照上述文章配置即可斩个,但是該配置只可以在測試環(huán)境下使用,生產(chǎn)環(huán)境不可以使用驯杜。參考配置構建變體配置兩份文件即可受啥。兜兜轉轉感覺早就應該想到是http問題才是,卻遲鈍了8胄摹9鼍帧!之前公司使用MediaPlayer播放的也是https的地址顽频。
??在實現(xiàn)過程中藤肢,發(fā)現(xiàn)如果在使用stop方法停止音頻播放時,此時斷網(wǎng)糯景,調用異步準備方法可以重用剛剛緩沖好的音頻內容嘁圈,如果在stop后每次都重新設置新的數(shù)據(jù)源地址則都會重新從網(wǎng)絡獲取新的音頻數(shù)據(jù)。在現(xiàn)有實現(xiàn)中莺奸,在每次播放器回調onError方法時都重新創(chuàng)建一個新的MediaPlayer丑孩,雖然根據(jù)官網(wǎng)文章得知可以reset,但是畢竟還未經(jīng)測試灭贷,出錯情況也不好測試温学,不清楚實際情況如何,后面再做測試討論甚疟。
??另外附上整理的MediaPlayer管理類源碼給需要的小伙伴仗岖,如果發(fā)現(xiàn)使用中有問題,可以留言修改:

package com.khronos.components.media;

import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.text.TextUtils;

import com.orhanobut.logger.Logger;

import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 只支持音頻播放
 * 結合MediaPlayer生命周期
 */
public class MediaPlayerManager {

    public static final String TAG = "MediaPlayerManager";
    
    private static final int STATE_UNINITIALIZED = 0;
    private static final int STATE_IDLE = 1;
    private static final int STATE_INITIALIZED = 2;
    private static final int STATE_PREPARING = 3;
    private static final int STATE_PREPARED = 4;
    private static final int STATE_STARTED = 5;
    private static final int STATE_PAUSED = 6;
    private static final int STATE_COMPLETED = 7;
    private static final int STATE_STOPED = 8;
    private static final int STATE_ERROR = 9;
    private static final int STATE_END = 10;


    private static volatile MediaPlayerManager sMediaPlayerManager;

    private MediaPlayer mMediaPlayer;

    private volatile int mCurrentSate = STATE_UNINITIALIZED;

    private MediaPlayer.OnCompletionListener mCompletionListener;
    private MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListener;
    private MediaPlayer.OnSeekCompleteListener mSeekCompleteListener;
    private MediaPlayer.OnPreparedListener mPreparedListener;
    private MediaPlayer.OnErrorListener mOnErrorListener;
    private ScheduledExecutorService mScheduledExecutorService;
    private SendPlayProgress mSendPlayProgress = new SendPlayProgress();
    private OnMediaProgressUpdateListener mOnMediaProgressUpdateListener;
    private Handler mMainHandler = new Handler();
    private MainRunnable mMainRunnable = new MainRunnable();
    private String mPlayingUrl;

    /**
     * 初始化進度回調線程池
     */
    private void initProgressSchedule() {
        if (mOnMediaProgressUpdateListener == null) {
            return;
        }
        if (mScheduledExecutorService == null || mScheduledExecutorService.isShutdown()) {
            mScheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
            mScheduledExecutorService.scheduleAtFixedRate(mSendPlayProgress, 0, 2, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 銷毀進度回調線程池
     */
    private void destroyProgressSchedule() {
        if (mScheduledExecutorService != null && !mScheduledExecutorService.isShutdown()) {
            mScheduledExecutorService.shutdownNow();
        }
    }

    public void setOnMediaProgressUpdateListener(OnMediaProgressUpdateListener onMediaProgressUpdateListener) {
        mOnMediaProgressUpdateListener = onMediaProgressUpdateListener;
    }

    public void setOnErrorListener(MediaPlayer.OnErrorListener onErrorListener) {
        mOnErrorListener = onErrorListener;
    }

    /**
     * 進度更新回調
     */
    interface OnMediaProgressUpdateListener {
        /**
         * 進度更新
         *
         * @param progress 進度
         */
        void onProgress(int progress);
    }

    /**
     * 播放器單例
     *
     * @return 播放器
     */
    public static MediaPlayerManager getInstance() {
        if (sMediaPlayerManager == null) {
            synchronized (MediaPlayerManager.class) {
                if (sMediaPlayerManager == null) {
                    sMediaPlayerManager = new MediaPlayerManager();
                }
            }
        }
        return sMediaPlayerManager;
    }

    private MediaPlayerManager() {
        init();
    }

    /**
     * 初始化播放器
     */
    private synchronized void init() {
        release();
        mMediaPlayer = new MediaPlayer();
        mCurrentSate = STATE_IDLE;
        mMediaPlayer.setOnBufferingUpdateListener(mOnBufferingUpdateListenerInternal);
        mMediaPlayer.setOnCompletionListener(mOnCompletionListenerInternal);
        mMediaPlayer.setOnErrorListener(mOnErrorListenerInternal);
        mMediaPlayer.setOnInfoListener(mOnInfoListenerInternal);
        mMediaPlayer.setOnPreparedListener(mOnPreparedListenerInternal);
        mMediaPlayer.setOnSeekCompleteListener(mOnSeekCompleteListenerInternal);
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    }

    /**
     * 每次播放音頻都會創(chuàng)建一個新的播放器,并且釋放上一個播放器
     *
     * @param url 地址 支持本地地址或者網(wǎng)絡地址
     */
    public synchronized void playOnCreate(String url) {
        if (mCurrentSate == STATE_PAUSED) {
            start();
            return;
        }
        init();
        setDataSource(url);
        initProgressSchedule();
    }

    /**
     * 每次使用同一個播放器播放音頻
     *
     * @param url 地址 支持本地地址或者網(wǎng)絡地址
     */
    public synchronized void playOnOne(String url) {
        if (TextUtils.isEmpty(url)) {
            return;
        }
        //已經(jīng)結束或者已經(jīng)有正在播放的
        if (mCurrentSate == STATE_STARTED) {
            return;
        }
        //未創(chuàng)建MediaPlayer對象或者已經(jīng)釋放了
        if (mCurrentSate == STATE_UNINITIALIZED || mCurrentSate == STATE_END) {
            init();
        }
        //手動停止的情況下
        if (mCurrentSate == STATE_STOPED) {
            //如果此時斷網(wǎng)然后重新播放同一個地址
            if (TextUtils.equals(mPlayingUrl, url)) {
                //使用異步準備方法可以繼續(xù)播放览妖,否則同個設置數(shù)據(jù)源的方式無法使用本地緩沖好的音頻內容繼續(xù)播放
                mMediaPlayer.prepareAsync();
                mCurrentSate = STATE_PREPARING;
                return;
            }
            reset();
        }
        //暫停播放
        if (mCurrentSate == STATE_PAUSED) {
            if (TextUtils.equals(mPlayingUrl, url)) {
                start();
                return;
            }
            reset();
        }
        //設置數(shù)據(jù)源
        mPlayingUrl = url;
        setDataSource(url);
        initProgressSchedule();
    }

    private MediaPlayer.OnSeekCompleteListener mOnSeekCompleteListenerInternal = new MediaPlayer.OnSeekCompleteListener() {
        @Override
        public void onSeekComplete(MediaPlayer mp) {
            Logger.t(TAG).e("onSeekComplete:mp = " + mp);
            if (mSeekCompleteListener != null) {
                mSeekCompleteListener.onSeekComplete(mp);
            }
        }
    };

    private MediaPlayer.OnBufferingUpdateListener mOnBufferingUpdateListenerInternal = new MediaPlayer.OnBufferingUpdateListener() {
        @Override
        public void onBufferingUpdate(MediaPlayer mp, int percent) {
            if (mOnBufferingUpdateListener != null) {
                mOnBufferingUpdateListener.onBufferingUpdate(mp, percent);
            }
            Logger.t(TAG).i("onBufferingUpdate:mp = " + mp + ", percent = " + percent);
        }
    };

    private MediaPlayer.OnCompletionListener mOnCompletionListenerInternal = new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            mCurrentSate = STATE_COMPLETED;
            stop();
            Logger.t(TAG).i("onCompletion:mp = " + mp);
            if (mCompletionListener != null) {
                mCompletionListener.onCompletion(mp);
            }
        }
    };

    /**
     * 返回true轧拄,在出錯的情況下將不會在繼續(xù)調用{@link MediaPlayer.OnCompletionListener#onCompletion(MediaPlayer)}
     */
    private MediaPlayer.OnErrorListener mOnErrorListenerInternal = new MediaPlayer.OnErrorListener() {
        @Override
        public boolean onError(MediaPlayer mp, int what, int extra) {
            mCurrentSate = STATE_ERROR;
            getInstance().release();
            Logger.t(TAG).e("onError:mp = " + mp + ", what = " + what + ", extra = " + extra);
            if (mOnErrorListener != null) {
                mOnErrorListener.onError(mp, what, extra);
            }
            return true;
        }
    };

    private MediaPlayer.OnInfoListener mOnInfoListenerInternal = new MediaPlayer.OnInfoListener() {
        @Override
        public boolean onInfo(MediaPlayer mp, int what, int extra) {
            Logger.t(TAG).i("onInfo:mp = " + mp + ", what = " + what + ", extra = " + extra);
            return false;
        }
    };

    private MediaPlayer.OnPreparedListener mOnPreparedListenerInternal = new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            mCurrentSate = STATE_PREPARED;
            getInstance().start();
            Logger.t(TAG).i("onPrepared:mp = " + mp);
            if (mPreparedListener != null) {
                mPreparedListener.onPrepared(mp);
            }
        }
    };

    /**
     * 播放器實例
     */
    public synchronized void release() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
            mCurrentSate = STATE_END;
            mMediaPlayer = null;
        }
        mPlayingUrl = "";
        destroyProgressSchedule();
    }

    /**
     * 設置完成回調
     *
     * @param listener 完成回調
     */
    public void setCompleteListener(MediaPlayer.OnCompletionListener listener) {
        mCompletionListener = listener;
    }

    /**
     * 設置緩存回調
     *
     * @param listener 緩存回調
     */
    public void setOnBufferingUpdateListener(MediaPlayer.OnBufferingUpdateListener listener) {
        mOnBufferingUpdateListener = listener;
    }

    /**
     * 設置滑動條
     *
     * @param listener 滑條監(jiān)聽
     */
    public void setSeekCompleteListener(MediaPlayer.OnSeekCompleteListener listener) {
        mSeekCompleteListener = listener;
    }

    /**
     * 設置播放準備完成
     *
     * @param preparedListener 播放準備完成
     */
    public void setPreparedListener(MediaPlayer.OnPreparedListener preparedListener) {
        this.mPreparedListener = preparedListener;
    }

    /**
     * 為了重用一個處于Error狀態(tài)的MediaPlayer對象,可以調用reset()方法來把這個對象恢復成Idle狀態(tài)讽膏。
     */
    private synchronized void reset() {
        try {
            destroyProgressSchedule();
            mMediaPlayer.reset();
            mCurrentSate = STATE_IDLE;
        } catch (Exception e) {
            Logger.t(TAG).e("reset:= " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 設置數(shù)據(jù)源檩电,可以說本地文件路徑,也可以是在線音頻流url府树;
     *
     * @param url 地址
     */
    private synchronized void setDataSource(String url) {
        try {
            if (mCurrentSate != STATE_IDLE) {
                return;
            }
            mMediaPlayer.setDataSource(url);
//            Class<MediaPlayer> clazz = MediaPlayer.class;
//            Method method = clazz.getDeclaredMethod("setDataSource", String.class, Map.class);
//            method.invoke(mMediaPlayer, path, new HashMap<String, String>());
            mCurrentSate = STATE_INITIALIZED;
            mMediaPlayer.prepareAsync();
            mCurrentSate = STATE_PREPARING;
        } catch (IllegalStateException e) {
            e.printStackTrace();
            Logger.t(TAG).e("setDataSource:= " + e.getMessage());
        } catch (IOException e) {
            Logger.t(TAG).e("setDataSource:= " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 異步準備方法只有在 {@link #STATE_INITIALIZED} {@link #STATE_STOPED}狀態(tài)下可以調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanPrepareAsync() {
        return mCurrentSate == STATE_INITIALIZED || mCurrentSate == STATE_STOPED;
    }

    /**
     * 如果之前pasue的話俐末,從pasue處播放;否則奄侠,從bengin處播放
     *
     * @return 開始
     */
    public synchronized boolean start() {
        try {
            if (!isCanStart()) {
                return false;
            }
            if (null != mMediaPlayer) {
                mMediaPlayer.start();
            }
            mCurrentSate = STATE_STARTED;
            return true;
        } catch (IllegalStateException e) {
            Logger.t(TAG).e("start ex " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            Logger.t(TAG).e("start ex " + e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 啟動方法只有在
     * {@link #STATE_PREPARED}
     * {@link #STATE_PAUSED}
     * {@link #STATE_PAUSED}
     * 狀態(tài)下可以調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanStart() {
        return mCurrentSate == STATE_PREPARED || mCurrentSate == STATE_PAUSED || mCurrentSate == STATE_COMPLETED;
    }

    /**
     * 暫停方法只有在 {@link #STATE_STARTED} 狀態(tài)下可以調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanPause() {
        return mCurrentSate == STATE_STARTED;
    }

    /**
     * 暫停
     */
    public synchronized void pause() {
        if (!isCanPause()) {
            return;
        }
        mMediaPlayer.pause();
        mCurrentSate = STATE_PAUSED;
    }

    /**
     * 檢查相關方法在生命周期內是否可以調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanCall() {
        if (mCurrentSate == STATE_IDLE || mCurrentSate == STATE_END || mCurrentSate == STATE_PREPARING) {
            return false;
        }
        return true;
    }

    /**
     * 停止
     *
     * @return 停止
     */
    public synchronized boolean stop() {
        try {
            if (!isCanStop()) {
                return false;
            }
            mMediaPlayer.stop();
            mCurrentSate = STATE_STOPED;
            return true;
        } catch (IllegalStateException e) {
            Logger.t(TAG).e("stop ex " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            Logger.t(TAG).e("stop ex " + e.getMessage());
            e.printStackTrace();
        }
        destroyProgressSchedule();
        return false;
    }

    /**
     * 暫停方法只需要在
     * {@link #STATE_PAUSED}
     * {@link #STATE_STARTED}
     * {@link #STATE_PREPARED}狀態(tài)下調用
     * {@link #STATE_COMPLETED}狀態(tài)下調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanStop() {
        return mCurrentSate == STATE_PAUSED || mCurrentSate == STATE_STARTED || mCurrentSate == STATE_PREPARED
                || mCurrentSate == STATE_COMPLETED;
    }

    /**
     * 獲取當前狀態(tài)
     *
     * @return 當前狀態(tài)
     */
    public synchronized int getCurrentState() {
        return mCurrentSate;
    }

    /**
     * 是否暫停狀態(tài)
     *
     * @return 是否暫停
     */
    public synchronized boolean isPauseState() {
        return mCurrentSate == STATE_PAUSED;
    }

    /**
     * 是否已經(jīng)開啟的狀態(tài)
     *
     * @return 是否開啟
     */
    public synchronized boolean isStartedState() {
        return mCurrentSate == STATE_PREPARED;
    }

    /**
     * 設置左右聲音音量
     *
     * @param left  左音道
     * @param right 右音道
     */
    public synchronized void setVolume(float left, float right) {
        if (!isCanSet()) {
            return;
        }
        mMediaPlayer.setVolume(left, right);
    }

    /**
     * 獲取當前進度卓箫,單位是毫秒
     *
     * @return 進度
     */
    private synchronized int getCurrentPosition() {
        return mMediaPlayer.getCurrentPosition();
    }

    /**
     * 獲取時長,單位是毫秒垄潮;如果不可用烹卒,則返回-1闷盔;
     *
     * @return 時長
     */
    private synchronized int getDuration() {
        return mMediaPlayer.getDuration();
    }

    /**
     * 是否循環(huán)
     *
     * @param looping 是否循環(huán)
     */
    public synchronized void setLooping(boolean looping) {
        if (!isCanSet()) {
            return;
        }
        mMediaPlayer.setLooping(looping);
    }

    /**
     * 只有在 {@link #STATE_PREPARED}狀態(tài)下才可以修改播放器屬性
     *
     * @return true 可以調用 false 不可以調用
     */
    private synchronized boolean isCanSet() {
        return mCurrentSate == STATE_PREPARED;
    }


    /**
     * 是否循環(huán)
     *
     * @return 是否循環(huán)
     */
    public synchronized boolean isLooping() {
        return mMediaPlayer.isLooping();
    }

    /**
     * isPlaying()方法可以被調用來測試某個MediaPlayer對象是否在Started狀態(tài)。
     *
     * @return 是否在播放
     */
    public synchronized boolean isPlaying() {
        if (mCurrentSate == STATE_END) {
            return false;
        }
        try {
            return mMediaPlayer.isPlaying();
        } catch (Exception e) {
            Logger.t(TAG).e("isPlaying ex " + e.getMessage());
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 拉動進度
     *
     * @param position 進度
     */
    public synchronized void seekTo(int position) {
        if (!isCanSeekTo()) {
            return;
        }
        mMediaPlayer.seekTo(position);
    }

    /**
     * 該方法只需要在
     * {@link #STATE_PAUSED}
     * {@link #STATE_COMPLETED}
     * {@link #STATE_PREPARED}狀態(tài)下調用
     *
     * @return true 可以調用 false 不可以調用
     */
    private boolean isCanSeekTo() {
        return mCurrentSate == STATE_PREPARED
                || mCurrentSate == STATE_PAUSED
                || mCurrentSate == STATE_COMPLETED
                || mCurrentSate == STATE_STARTED;
    }

    class MainRunnable implements Runnable {
        public int progress;

        @Override
        public void run() {
            if (mOnMediaProgressUpdateListener != null) {
                mOnMediaProgressUpdateListener.onProgress(progress);
            }
        }
    }

    private class SendPlayProgress implements Runnable {
        @Override
        public void run() {
            if (!isCanCall()) {
                return;
            }
            if (mMediaPlayer == null || !isPlaying()) {
                return;
            }
            mMainRunnable.progress = (int) ((getCurrentPosition() * 1.0f / getDuration() * 1.0f) * 100 + 0.9f);
            mMainHandler.post(mMainRunnable);

        }
    }

}

??如果需要順序播放多個音頻旅急,并且希望無縫切換則可以考慮在當前播放器播放音頻的時候去創(chuàng)建一個播放器預加載下一條音頻逢勾,在使用網(wǎng)易云音樂的測試時,音頻切換間隔在10幾毫秒的樣子坠非。如果不做預加載那么在順序加載播放的情況下敏沉,音頻切換間隔則在600+毫秒左右。如果音頻地址有問題或者因為網(wǎng)絡問題加載過慢炎码,則可以考慮加入超時機制加以判斷盟迟。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市潦闲,隨后出現(xiàn)的幾起案子攒菠,更是在濱河造成了極大的恐慌,老刑警劉巖歉闰,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辖众,死亡現(xiàn)場離奇詭異,居然都是意外死亡和敬,警方通過查閱死者的電腦和手機凹炸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來昼弟,“玉大人啤它,你說我怎么就攤上這事〔斩唬” “怎么了变骡?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芭逝。 經(jīng)常有香客問我塌碌,道長,這世上最難降的妖魔是什么旬盯? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任台妆,我火速辦了婚禮,結果婚禮上胖翰,老公的妹妹穿的比我還像新娘频丘。我一直安慰自己,他們只是感情好泡态,可當我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著迂卢,像睡著了一般某弦。 火紅的嫁衣襯著肌膚如雪桐汤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天靶壮,我揣著相機與錄音怔毛,去河邊找鬼。 笑死腾降,一個胖子當著我的面吹牛拣度,可吹牛的內容都是我干的。 我是一名探鬼主播螃壤,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼抗果,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奸晴?” 一聲冷哼從身側響起冤馏,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寄啼,沒想到半個月后逮光,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡墩划,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年涕刚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乙帮。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡杜漠,死狀恐怖,靈堂內的尸體忽然破棺而出蚣旱,到底是詐尸還是另有隱情碑幅,我是刑警寧澤,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布塞绿,位于F島的核電站沟涨,受9級特大地震影響,放射性物質發(fā)生泄漏异吻。R本人自食惡果不足惜裹赴,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诀浪。 院中可真熱鬧棋返,春花似錦、人聲如沸雷猪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽求摇。三九已至射沟,卻和暖如春殊者,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背验夯。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工猖吴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挥转。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓海蔽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绑谣。 傳聞我的和親對象是個殘疾皇子党窜,可洞房花燭夜當晚...
    茶點故事閱讀 45,055評論 2 355