Android API23升级到API28后低码率音频MediaPlayer.getDuration()返回-1问题
我之前也碰到过类似的升级问题,结合Android官方文档和实际测试,这个情况大概率是因为API 28及以上版本中,MediaPlayer对低码率音频的元数据解析逻辑变严格了——低码率音频的时长信息没法在setDataSource之后立即被解析出来,而旧版本API的处理逻辑更宽松,所以侥幸能拿到正确时长。
下面是几个经过验证的解决方案:
方案1:必须等MediaPlayer完全准备好再获取时长
MediaPlayer的很多核心操作(包括获取时长)都要求它进入Prepared状态才能正常工作。你之前的代码可能没等准备完成就调用getDuration(),在API 23中碰巧生效,但新版API对状态检查更严格,直接调用就会返回-1。
正确的写法应该用异步准备+监听回调:
MediaPlayer mp = new MediaPlayer(); mp.setAudioStreamType(AudioManager.STREAM_MUSIC); try { mp.setDataSource("你的低码率音频URL"); // 设置准备完成监听器 mp.setOnPreparedListener(mediaPlayer -> { // 此时MediaPlayer已就绪,安全获取时长 int duration = mediaPlayer.getDuration(); Log.d("MediaPlayer", "音频时长:" + duration + "ms"); // 可以在这里开始播放 mediaPlayer.start(); }); // 设置错误监听器,处理加载失败情况 mp.setOnErrorListener((mediaPlayer, what, extra) -> { Log.e("MediaPlayer", "准备失败:错误码=" + what + ", 额外信息=" + extra); return false; }); // 异步准备,避免阻塞主线程(网络音频尤其重要) mp.prepareAsync(); } catch (IOException e) { e.printStackTrace(); }
划重点:绝对不要在setDataSource之后直接调用getDuration(),一定要在OnPreparedListener的回调里执行这个操作。
方案2:改用ExoPlayer替代原生MediaPlayer
Google现在官方推荐用ExoPlayer作为媒体播放的首选方案,它对各种码率、格式的兼容性比原生MediaPlayer好太多,尤其是处理低码率、特殊编码的媒体文件时表现更稳定。
用ExoPlayer获取时长的简化示例:
// 初始化ExoPlayer SimpleExoPlayer player = new SimpleExoPlayer.Builder(context).build(); // 创建媒体源 MediaItem mediaItem = MediaItem.fromUri("你的低码率音频URL"); player.setMediaItem(mediaItem); // 添加状态监听 player.addListener(new Player.Listener() { @Override public void onPlaybackStateChanged(int playbackState) { if (playbackState == Player.STATE_READY) { // 获取时长(单位:毫秒) long duration = player.getDuration(); Log.d("ExoPlayer", "音频时长:" + duration + "ms"); } } }); // 开始准备 player.prepare();
ExoPlayer的解析逻辑更健壮,而且官方会持续更新维护,后续版本的兼容性更有保障,如果你经常处理各种媒体文件,建议直接切换到ExoPlayer。
方案3:手动解析音频元数据(仅复杂场景备用)
如果上面两个方案都不适用,你可以考虑手动解析音频文件的时长。比如MP3文件,可以读取ID3标签或者计算音频帧的总长度来推导时长,但这种方式需要处理文件IO,网络音频还得先下载部分数据,实现起来比较繁琐,一般不推荐作为首选方案。
内容的提问来源于stack exchange,提问作者Attaullah

