Delphi FireMonkey安卓应用多次播放音频后失效报错问题
Delphi FireMonkey 安卓端音频播放累计200次左右失效问题排查
故障根因
你遇到的两类异常、播放到固定次数后无声的问题,本质是FireMonkey框架对安卓原生MediaPlayer的资源封装存在缺陷,未正确释放底层播放器占用的文件句柄、音频硬件资源,累计触发安卓系统单进程MediaPlayer实例配额上限(安卓原生系统默认单进程该类实例配额约为200个),后续新建实例、加载数据源时就会抛出对应错误。
两个异常对应的直接触发场景:
java.lang.RuntimeException: setDataSource failed: status = 0x800000000:进程持有的无效MediaPlayer实例数达到上限,或实例已处于销毁状态,无法绑定音频数据源java.io.IOException: Prepare failed.: status=0x1:系统资源耗尽后,新的MediaPlayer实例无法分配到解码所需的硬件/内存资源,准备流程失败
现有代码的问题
- 设计时预置
TMediaPlayer的实现:每次播放前重复给FileName赋值,框架内部会新建原生MediaPlayer实例加载资源,但播放完成后不会主动触发底层资源释放,旧实例持续占用配额,累计到上限后失效。 - 运行时动态创建立即销毁的实现:
Play方法是异步非阻塞调用,调用后音频还未完成加载、启动播放就同步执行mp.Free,Delphi层对象被销毁后,底层已经初始化的原生MediaPlayer实例会变成无引用的泄漏对象,无法被系统回收,资源泄漏速度比第一种方案更快。
修复方案
- 固定复用单实例,避免频繁创建销毁
TMediaPlayer
音频文件固定的场景下,初始化时只创建一次TMediaPlayer、赋值一次文件路径,绑定播放完成事件主动释放底层资源,避免资源泄漏,参考实现:// 窗体类私有变量提前声明 FWarningPlayer: TMediaPlayer; procedure TMainForm.FormCreate(Sender: TObject); begin FWarningPlayer := TMediaPlayer.Create(Self); FWarningPlayer.Parent := Self; FWarningPlayer.OnDidFinishPlaying := PlayerDidFinish; // 初始化时仅赋值一次文件路径 FWarningPlayer.FileName := TPath.Combine(TPath.GetPublicPath, 'sound/Warning.3gp'); end; procedure TMainForm.PlayerDidFinish(Sender: TObject); begin // 播放完成后主动调用Clear,释放底层安卓原生MediaPlayer实例 (Sender as TMediaPlayer).Clear; end; // 需要播放时调用 procedure TMainForm.PlayWarning; begin // Clear后Media为空,重新赋值路径触发加载 if FWarningPlayer.Media = nil then FWarningPlayer.FileName := TPath.Combine(TPath.GetPublicPath, 'sound/Warning.3gp'); if FWarningPlayer.Media <> nil then FWarningPlayer.Play; end; - 禁止在调用
Play后立即释放TMediaPlayer对象。如果确实需要动态创建实例,必须等OnDidFinishPlaying事件触发、确认播放完全结束后,再执行对象释放操作。 - 如果上述方案仍存在偶发资源泄漏,可以直接绕开FMX封装的
TMediaPlayer,通过JNI调用安卓原生JMediaPlayer类自行实现播放逻辑,手动控制setDataSource、prepare、start、release全流程,从根源规避框架层的资源泄漏缺陷。注意原生实现中每次播放结束必须调用release方法释放实例,不要长期持有闲置播放器实例。
内容的提问来源于stack exchange,提问作者mesutuk
相关产品推荐
相关产品推荐

