子类化AVPlayer时,是否有比通知中心更直接的播放结束监听方式?
替代AVPlayer全局通知的播放结束监听方案
AVPlayer/AVQueuePlayer并没有提供可直接重写的方法来捕获播放结束事件,但你可以用以下几种更严谨、低耦合的方式替代全局通知:
1. 为每个AVPlayerItem单独绑定通知监听
在子类中管理每个添加的item的通知监听,只响应自己管辖的item的结束事件,避免全局通知的广播干扰:
class FancyAVPlayer: AVQueuePlayer { private var observationTokens = [NSObjectProtocol]() override init(items: [AVPlayerItem]) { super.init(items: items) items.forEach(setupItemObservers) } override func insert(_ item: AVPlayerItem, after afterItem: AVPlayerItem?) { super.insert(item, after: afterItem) setupItemObservers(item) } override func remove(_ item: AVPlayerItem) { super.remove(item) removeItemObservers(item) } private func setupItemObservers(_ item: AVPlayerItem) { let token = NotificationCenter.default.addObserver( forName: .AVPlayerItemDidPlayToEndTime, object: item, queue: .main ) { [weak self] _ in self?.handleItemEnded(item) } observationTokens.append(token) } private func removeItemObservers(_ item: AVPlayerItem) { observationTokens.removeAll { token in if let observer = token as? NSNotificationObserver, observer.object as? AVPlayerItem == item { NotificationCenter.default.removeObserver(observer) return true } return false } } private func handleItemEnded(_ item: AVPlayerItem) { print("AVPlayer subclass, item \(item) ended") // 这里可以扩展自定义回调逻辑,比如闭包回调、代理方法 } deinit { observationTokens.forEach(NotificationCenter.default.removeObserver) } }
2. 使用Combine框架实现响应式监听(Swift 5.1+)
利用Combine的发布者机制,监听currentItem变化并绑定结束通知,代码更简洁且自动管理生命周期:
import Combine class FancyAVPlayer: AVQueuePlayer { private var cancellables = Set<AnyCancellable>() override init(items: [AVPlayerItem]) { super.init(items: items) setupCombineObservers() } override func insert(_ item: AVPlayerItem, after afterItem: AVPlayerItem?) { super.insert(item, after: afterItem) setupCombineObservers() } private func setupCombineObservers() { self.publisher(for: \.currentItem) .compactMap { $0 } .flatMap { item in NotificationCenter.default.publisher( for: .AVPlayerItemDidPlayToEndTime, object: item ) } .sink { [weak self] _ in self?.handleItemEnded() } .store(in: &cancellables) } private func handleItemEnded() { print("AVPlayer subclass, playback ended via Combine") } deinit { cancellables.removeAll() } }
3. 利用边界时间观察者监听播放结束点
通过设置播放进度的边界点,当播放到接近结束时间时触发回调:
class FancyAVPlayer: AVQueuePlayer { private var boundaryObservers = [Any]() override func insert(_ item: AVPlayerItem, after afterItem: AVPlayerItem?) { super.insert(item, after: afterItem) setupBoundaryObserver(for: item) } private func setupBoundaryObserver(for item: AVPlayerItem) { // 稍微延后结束时间,避免因时间精度问题提前触发 let endTime = CMTimeAdd(item.duration, CMTimeMakeWithSeconds(0.1, preferredTimescale: 1000)) let times = [NSValue(time: endTime)] let observer = addBoundaryTimeObserver(forTimes: times, queue: .main) { [weak self] in guard let self = self, self.currentItem == item else { return } self.handleItemEnded() } boundaryObservers.append(observer) } private func handleItemEnded() { print("AVPlayer subclass, playback ended via boundary observer") } deinit { boundaryObservers.forEach { removeTimeObserver($0) } } }
需要注意的是,AVPlayer的设计中并没有开放可重写的播放结束回调方法,苹果更倾向于通过通知、KVO或Combine这类响应式机制来暴露播放状态变化。上面的方案都比全局通知更合理,因为它们只关注当前播放器管理的媒体项,减少了不必要的耦合。
内容的提问来源于stack exchange,提问作者Fattie
相关产品推荐
相关产品推荐

