You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NativeScript插件中为iOS的AVPlayer添加事件观察者

在NativeScript iOS中用AVPlayer处理流媒体播放事件与变量管理(解决崩溃问题)

我明白你现在的处境——nativescript-audio在Android上跑的好好的,到iOS因为AVAudioPlayer不支持流媒体不得不换成AVPlayer,结果折腾事件监听和变量管理的时候还崩了。别着急,咱们一步步来解决这个问题。

一、AVPlayer播放结束事件的正确监听方式

AVPlayer本身没有直接的“播放结束”回调,得通过它的AVPlayerItem来监听,主要有两种可靠方式:

  • 利用通知中心监听AVPlayerItemDidPlayToEndTimeNotification:这是系统提供的最直接的播放结束通知
  • 监听AVPlayerItem的status属性:用来处理播放出错、缓冲完成等状态变化,避免因状态异常触发崩溃

二、变量与观察者的安全管理(避免崩溃的核心)

崩溃大概率和观察者未正确移除、变量引用不当有关,这里给你几个关键要点:

  • 务必在组件销毁时清理所有观察者:NativeScript的页面/组件都有unloaded或destroyed事件,一定要在这里移除通知监听和KVO观察者,不然会导致内存泄漏甚至野指针崩溃
  • 不要直接持有未初始化的实例引用:在创建AVPlayer/AVPlayerItem前要做空值判断,避免空指针调用
  • UI操作必须在UI线程执行:如果事件回调里要更新UI(比如播放结束后切换按钮状态),记得用Application.runOnUIThread()包裹

三、完整代码示例(TypeScript)

import { Application, Page } from "@nativescript/core";
import { ios as iosUtils } from "@nativescript/core";

export class RadioPlayer {
  private avPlayer: AVPlayer;
  private playerItem: AVPlayerItem;
  private endTimeObserver: NSObject;

  constructor(private page: Page) {
    // 绑定页面卸载事件,确保资源能被清理
    this.page.on("unloaded", () => this.cleanup());
  }

  initPlayer(streamUrl: string) {
    // 创建AVPlayerItem实例
    const url = NSURL.URLWithString(streamUrl);
    this.playerItem = AVPlayerItem.alloc().initWithURL(url);

    // 监听播放结束通知
    this.endTimeObserver = NSNotificationCenter.defaultCenter.addObserverForNameObjectQueueUsingBlock(
      AVPlayerItemDidPlayToEndTimeNotification,
      this.playerItem,
      null,
      (notification) => {
        // 播放结束后的业务逻辑
        Application.runOnUIThread(() => {
          console.log("播放结束");
          // 这里可以更新UI,比如切换播放按钮状态
        });
      }
    );

    // 监听AVPlayerItem的状态变化,处理缓冲、出错等场景
    this.playerItem.addObserverForKeyPathOptionsContext(
      this,
      "status",
      NSKeyValueObservingOptions.New,
      null
    );

    // 初始化AVPlayer并开始播放
    this.avPlayer = AVPlayer.alloc().initWithPlayerItem(this.playerItem);
    this.avPlayer.play();
  }

  // KVO回调处理状态变化
  observeValueForKeyPathOfObjectChangeContext(keyPath: string, object: any, change: NSDictionary, context: any) {
    if (keyPath === "status") {
      const status = this.playerItem.status;
      if (status === AVPlayerStatus.Failed) {
        console.error("播放出错:", this.playerItem.error?.localizedDescription);
      } else if (status === AVPlayerStatus.ReadyToPlay) {
        console.log("缓冲完成,开始播放");
      }
    }
  }

  // 清理资源,彻底避免崩溃
  cleanup() {
    if (this.endTimeObserver) {
      NSNotificationCenter.defaultCenter.removeObserver(this.endTimeObserver);
      this.endTimeObserver = null;
    }
    if (this.playerItem) {
      this.playerItem.removeObserverForKeyPathContext(this, "status", null);
      this.playerItem = null;
    }
    if (this.avPlayer) {
      this.avPlayer.pause();
      this.avPlayer = null;
    }
  }
}

四、常见崩溃原因排查

  • 忘记移除观察者:如果组件销毁后观察者还存在,当AVPlayerItem触发事件时会访问已销毁的组件实例,直接引发崩溃——所以cleanup方法一定要在组件卸载时调用
  • 空指针访问:比如在initPlayer前就调用了avPlayer.play(),或者playerItem未初始化就添加观察者,一定要确保实例创建完成后再操作
  • KVO使用不当:KVO的回调方法observeValueForKeyPathOfObjectChangeContext必须正确实现,否则会触发系统异常

内容的提问来源于stack exchange,提问作者Ken

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 07:52:07