問題背景
有一個公司內部的同學反饋如下問題:
系統版本:iOS 15 beta4
app版本:番茄小說 4.8.0
問題描述:聽書功能,在CarPlay的時候,5分鐘左右就會自動沒聲音,需要手動解鎖iPhone,并且把小說切到前臺,才能恢復(期間應該還在讀,只是沒聲音)
額外信息:QQ音樂播放正常,問題可能和切章場景有關。
問題分析
在接到反饋之后,抽取其中關鍵信息:iOS15、480版本、聽書、Carplay、后臺播放5分鐘沒聲音、切換前臺恢復播放。
480版本在上線之前有經過充分的測試,正常后臺播放一段時間沒聲音的情況并沒有出現。iOS 15是蘋果還沒有發布的測試版本(問題出現時iOS 15還未發布),QA在蘋果發布iOS 15的beta版本時都會進行測試,也沒有出現過這個問題。所以最大的可能性應該是和Carplay有關,這屬于qa和rd都關注比較少的場景。
通過網上相關carplay適配文章使用 CarPlay 車載系統為你的 App 提速 - 開發者頭條,可以知道App需要使用UIScene才能支持CarPlay框架,目前小說還未支持Carplay。所以應該是iphone連接到車載系統之后,用默認的音樂播放協議。
問題排查
通過真機和真車測試,發現問題現象是在后臺長時間播放時,可能會播著播著就沒有聲音,但是打開App之后能恢復播放。
升級最新的MacOS系統和Xcode,用調試功能進行定位。
發現問題復現之后,日志會大量輸出:
2021-08-23 16:18:48.176082+0800 Reading[1724:1236625] [aurioc] AURemoteIO.cpp:1663 AUIOClient_StartIO failed (560557684)
2021-08-23 16:18:48.199090+0800 Reading[1724:1236625] [aurioc] AURemoteIO.cpp:1663 AUIOClient_StartIO failed (560557684)
2021-08-23 16:18:48.222265+0800 Reading[1724:1236625] [aurioc] AURemoteIO.cpp:1663 AUIOClient_StartIO failed (560557684)
2021-08-23 16:18:48.245355+0800 Reading[1724:1236625] [aurioc] AURemoteIO.cpp:1663 AUIOClient_StartIO failed (560557684)
有效信息是AURemoteIO和AUIOClient_StartIO failed (560557684)。
560557684應該是一個OSStatus的錯誤碼,通過錯誤碼分析可以知道對應錯誤碼含義:
根據AVAudioSessionErrorCodeCannotInterruptOthers,可以在XCode找到對應的描述是:
An error code that indictates an attempt to make a nonmixable audio session active while the app was in the background.
該錯誤發生在App后臺時嘗試設置AudioSession為Active。但是番茄小說App在音頻播放時,AudioSession是處于Active狀態。結合問題是出現在章節切換的場景,猜測問題可能是縮小為章節切換時,AudioSession被設置為InActive的狀態。
于是Hook了系統的setActive方法,在切后臺之后關注active的值變化。
實際運行的時候,發現猜測并不正確,所有的Active都設置為YES。
為了避免有其他業務邏輯修改到Category和Active狀態,增加更多的嘗試情況:在開始播放之前和開始播放之后,把Category設置為Playback并且將AudioSession設置為Active。
但是切換章節的時候,仍然出現了AUIOClient_StartIO failed (560557684)
的錯誤,至此問題排查有些陷入僵局。中間嘗試過App歷史版本、播放器SDK升級版本等,發現都會存在該異常現象。
問題確認
在對比其他App在同樣環境、同樣操作的過程中,發現了一個現象:
下面是正常后臺播放章節的狀態欄。
但是在Carplay的情況下,當異常暫停問題出現的時候,上面的播放狀態區域會變成下面的“未在播放”:
這個顯示區域是由MPRemoteCommandCenter管理,工程中主要有兩個方法會控制該類,分別是remoteAddCommand和remoteRemoveCommand。給兩個方法增加斷點之后,觸發切換章節。
可以發現在切換章節的時候,每次都會觸發一次remoteRemoveCommand,再觸發一次remoteAddCommand,這個邏輯看起來正常,并且也在線上持續運行較長時間。考慮到異常問題出現在切換章節時,而正常播放的時候并沒有這個現象,為了找到切換章節和正常播放的區別所在,嘗試將remoteRemoveCommand注釋。結果在注釋掉這行代碼之后,運行正常。
結合業務場景判斷沒必要每次切章的時候調用remoteRemoveCommand,可以在關閉懸浮窗時,即正常結束播放場景,再調用remoteRemoveCommand即可。沒找到如何識別Carplay連接的判斷,故而對該修復增加開關,僅對iOS 15生效。
問題總結
問題跟進過程中在構建編譯環境、復現問題花費較長時間,在復現問題之后又無法直接定位到原因。主要原因還是在于運行環境不好搭建,模擬器的Carplay并沒有出現異常,僅在支持Carplay的車子上會出現該問題。所以每次需要到車上調試,定位效率比較低。
雖然問題初步解決,但是查閱較多資料也沒有找到類似異常問題,也沒了解到iOS系統在Carplay的后臺播放播放場景有何不同。考慮到已經投入較多時間并且當前還未適配Carplay,繼續深入該問題的性價比不高。