0x00 進退兩難
在利用
AVPlayer
播放網絡 mp3 時,因為有監聽狀态,是以移除監聽是必須的
不然會出現以下錯誤:
An instance 0x174016100 of class AVPlayerItem was deallocated while key value observers were still registered with it.
不移除觀察者時,導緻的崩潰:
Trapped uncaught exception 'NSInternalInconsistencyException', reason: 'An instance 0x174016100 of class AVPlayerItem was deallocated while key value observers were still registered with it. Current observation info: <NSKeyValueObservationInfo 0x1708215a0> (
<NSKeyValueObservance 0x17484d530: Observer: 0x15c14ca00, Key path: status, Options: <New: YES, Old: YES, Prior: NO> Context: 0x0, Property: 0x174848d00>
<NSKeyValueObservance 0x17484e9a0: Observer: 0x15c14ca00, Key path: playbackLikelyToKeepUp, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174841f20>
<NSKeyValueObservance 0x174454610: Observer: 0x15c14ca00, Key path: playbackBufferEmpty, Options: <New: YES, Old: NO, Prior: NO> Context: 0x0, Property: 0x174849ff0>
)'
然而,有個蛋疼的操作出現了
當移除觀察者時
另外一個Bug出現了
Trapped uncaught exception 'NSInvalidArgumentException', reason: 'An instance of AVPlayer cannot remove a time observer that was added by a different instance of AVPlayer.'
這移除不行!
這不移除也不行!
這要怎麼玩??
0x01 情況分析
1.播放音頻
1.1移除前一個音頻的監聽狀态(如果存在)
1.2添加新的監聽狀态
2.狀态監聽回調方法
2.1成功則播放
2.2失敗
2.2.1第1次失敗,嘗試再次播放 -> 走步驟1
2.2.2第2次失敗,移除監聽狀态
就是在步驟
2.2.2
,出現了進退兩難的情況
0x02 解決方案
既然移除是必須的
那就隻能走移除!
但是移除又會導緻:
Trapped uncaught exception 'NSInvalidArgumentException', reason: 'An instance of AVPlayer cannot remove a time observer that was added by a different instance of AVPlayer.'
隻能前進
如何後退呢?
如果你能想到的話
你已經知道我要說什麼了
如果你還沒想到
繼續往下看
iOS中有個捕獲異常,防止崩潰的機制:
@try @catch
@try {
// 可能存在異常
[self.player removeTimeObserver:self.timeObserver];
} @catch (NSException *exception) {
// 檢視異常
NSLog(@"RemoveObserver:%@",exception);
} @finally {
// 去掉 finally 也可以
}
雖然這個捕獲異常的機制
捕獲的異常有限
但這裡确實能
防止
應用崩潰
Nice!
0x03 後續
2020-07-28 18:04:36
在移除監聽時,應該判斷
timeObserver
是否存在
并在移除後
置空
- (void)removeObserver
{
if (self.player.currentItem) {
@try {
[self.player.currentItem removeObserver:self forKeyPath:@"status"];
[self.player.currentItem removeObserver:self forKeyPath:@"playbackLikelyToKeepUp"];
[self.player.currentItem removeObserver:self forKeyPath:@"playbackBufferEmpty"];
[self.player replaceCurrentItemWithPlayerItem:nil];
if (self.timeObserver) {
[self.player removeTimeObserver:self.timeObserver];
self.timeObserver = nil;
}
} @catch (NSException *exception) {
NSLog(@"removeObserver:%@",exception);
} @finally {
}
}
}
就是因為前面沒置空
是以導緻後面移除時
跟
AVPlayer
比對不上
0x04 繼續
2020-07-28 18:09:13
遇到一個加載 mp3 url 後
無法播放的Bug:
Error Domain=AVFoundationErrorDomain Code=-11819 "無法完成操作" UserInfo={NSLocalizedDescription=無法完成操作, NSLocalizedRecoverySuggestion=請稍後再試。}
接着,是以其他的 mp3 url 資源都無法播放了
所有報錯都是這樣:
Error Domain=AVFoundationErrorDomain Code=-11819 "無法完成操作" UserInfo={NSLocalizedDescription=無法完成操作, NSLocalizedRecoverySuggestion=請稍後再試。, NSURL=https://xxx/xxx/xxx.mp3, NSUnderlyingError=0x174a51670 {Error Domain=NSOSStatusErrorDomain Code=268435459 "(null)"}}
代碼使用了同一個
AVPlayer
執行個體
猜測原因是
AVPlayer
内部有緩存
導緻後面加載的 url 都不請求了
最終嘗試如下操作
解決了這個蛋疼的問題
在回調方法裡
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
針對播放失敗狀态所做的處理
if ([keyPath isEqualToString:@"status"]) {
switch ([[change valueForKey:@"new"] integerValue]) {
case AVPlayerItemStatusFailed: {
NSError * error = _player.currentItem.error;
if (error) {
NSInteger code = erro.code;
NSInteger count = erro.userInfo.count;
if (code == -11819 &&
count == 2) {
[self removeObserver];
_player = nil;
}
}
// 可以嘗試重新加載
// 或者其他操作
}
break;
}
}
0x05 再續
有些音頻即使再次加載
也還是不能播放
錯誤有2種:
第一種:-11800,AVErrorUnknown,未知錯誤 T_T
Error Domain=AVFoundationErrorDomain Code=-11800 "這項操作無法完成" UserInfo={NSLocalizedFailureReason=發生未知錯誤(-16041), NSLocalizedDescription=這項操作無法完成, NSUnderlyingError=0x2801d9a70 {Error Domain=NSOSStatusErrorDomain Code=-16041 "(null)"}}
第二種:byte range length mismatch T_T
Error Domain=NSURLErrorDomain Code=-1 "未知錯誤" UserInfo={NSLocalizedDescription=未知錯誤, NSUnderlyingError=0x283db2be0 {Error Domain=CoreMediaErrorDomain Code=-12939 "byte range length mismatch - should be length 16384 is length 24137" UserInfo={NSDescription=byte range length mismatch - should be length 16384 is length 24137, NSURL=http://xxx/xxx/xxx.mp3}}}