media 多媒体
media 多媒体
media 这组 API 目前主要围绕两件事:
- 播放音频
- 通知系统重新扫描某个文件,让它尽快出现在媒体库里
它同时提供了两套播放器思路:
- 一套是模块级的“共享音乐播放器”
- 一套是
new media.MediaPlayer()创建的独立播放器对象
如果你只是想播一首提示音、背景音,先用 media.playMusic(...) 那套最省事;如果你想自己控制多个播放器实例、单独控制 prepare / seek / release 这些动作,就看 media.MediaPlayer。
先记住这 10 条
media.playMusic(...)用的是模块级共享播放器,不是每次都 new 一个新的。media.pauseMusic()/resumeMusic()/stopMusic()/musicSeekTo()都是针对这一个共享播放器。media.getMusicDuration()和media.getMusicCurrentPosition()也是读取共享播放器状态。media.scanFile(path)只要路径非空就会返回true,它不保证文件真的存在。media.MediaPlayer是单独的播放器实例构造器。player.play(uri)内部会先reset(),再重新设置数据源和播放参数。player.setVolume(volume)最终会被夹在0 ~ 1之间。player.seekTo(ms)、player.prepare()、player.awaitForCompletion()这类方法都会阻塞当前脚本线程直到动作完成。player.release()之后再调用其他方法会直接抛错。- URI 既支持普通文件路径,也支持
file://、content://、android.resource://。
模块级共享播放器
下面这一组方法都操作同一个共享播放器实例。
media.scanFile(file)
作用
让系统重新扫描一个文件路径,尽快把它纳入媒体库。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
file | string | 文件路径;空字符串直接返回 false |
返回值
boolean
返回规则
| 场景 | 返回值 |
|---|---|
| 路径为空 | false |
| 路径非空 | true |
注意
当前实现只检查“路径是不是空”,不会先帮你判断文件是否真实存在。
示例
const ok = media.scanFile("/sdcard/Music/demo.mp3");
log(`scan=${ok}`);
media.playMusic(uri, volume?, looping?)
作用
用共享播放器播放音频。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
uri | string | 无 | 必填;空字符串会在播放器层抛错 |
volume | number | 1.0 | 建议 0 ~ 1 |
looping | boolean | false | 是否循环播放 |
返回值
共享的 MediaPlayer 包装对象。
支持的 URI / 路径形态
| 形态 | 示例 | 说明 |
|---|---|---|
| 普通文件路径 | "/sdcard/Music/demo.mp3" | 直接按文件路径处理 |
file:// | "file:///sdcard/Music/demo.mp3" | 会取真实文件路径 |
content:// | "content://media/external/audio/media/1" | 需要可用 Context |
android.resource:// | "android.resource://包名/资源id" | 需要可用 Context |
| 其他 URI | "https://..." | 直接交给 MediaPlayer.setDataSource(path) |
示例
media.playMusic("/sdcard/Music/demo.mp3");
media.playMusic("/sdcard/Music/bgm.mp3", 0.6, true);
media.musicSeekTo(positionMs)
作用
让共享播放器跳到指定毫秒位置。
参数
| 参数 | 类型 | 说明 |
|---|---|---|
positionMs | number | 毫秒数 |
返回值
boolean
返回规则
| 场景 | 返回值 |
|---|---|
| 共享播放器不存在 | false |
| 参数不是数字 | false |
| seek 成功 | true |
| seek 过程中出错 | false |
示例
media.playMusic("/sdcard/Music/demo.mp3");
sleep(2000);
media.musicSeekTo(10000);
media.pauseMusic()
作用
暂停共享播放器。
返回值
boolean
示例
media.pauseMusic();
media.resumeMusic()
作用
恢复共享播放器播放。
返回值
boolean
示例
media.resumeMusic();
media.stopMusic()
作用
停止共享播放器。
返回值
boolean
说明
它调用的是播放器实例的 stop()。停掉以后如果你还想继续播,最简单的做法是直接重新 playMusic(...)。
media.isMusicPlaying()
返回值
boolean
示例
if (media.isMusicPlaying()) {
log("music is playing");
}
media.getMusicDuration()
返回值
number
规则
- 没有共享播放器时返回
0 - 读取失败时也会回退成
0
media.getMusicCurrentPosition()
返回值
number
规则
- 没有共享播放器时返回
0 - 读取失败时也会回退成
0
media.MediaPlayer
这是独立播放器构造器。
最小写法
const player = new media.MediaPlayer();
你也可以不写 new,但从可读性来说,建议就当构造器来用。
独立播放器对象
下面这一节里的 player 都是指:
const player = new media.MediaPlayer();
player.play(uri, volume?, looping?)
作用
播放指定音频。
真实流程
内部会做:
ensureNotReleased()reset()setDataSource(uri)setVolume(...)setLooping(...)prepare()start()
返回值
播放器对象本身。
示例
const player = new media.MediaPlayer();
player.play("/sdcard/Music/demo.mp3", 0.8, false);
player.reset()
作用
重置播放器状态,并清掉本轮准备 / seek / 完成等待相关的内部状态。
返回值
播放器对象本身。
player.setDataSource(path)
作用
单独设置数据源。
返回值
播放器对象本身。
什么时候需要单独用
如果你想自己拆开流程:
const player = new media.MediaPlayer();
player
.setDataSource("/sdcard/Music/demo.mp3")
.setLooping(true)
.prepare()
.start();
注意
对于 content:// 和 android.resource://,当前需要有可用 Context,否则会抛异常。
player.setVolume(leftVolume, rightVolume?)
作用
设置左右声道音量。
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
leftVolume | number | 无 | 最终会夹到 0 ~ 1 |
rightVolume | number | leftVolume | 最终会夹到 0 ~ 1 |
返回值
播放器对象本身。
示例
player.setVolume(0.5);
player.setVolume(0.8, 0.2);
player.setLooping(looping)
作用
设置是否循环。
返回值
播放器对象本身。
player.prepare()
作用
异步准备并阻塞等待准备完成。
返回值
播放器对象本身。
说明
虽然底层用的是 prepareAsync(),但脚本层这个方法会一直等到准备完成,所以你可以把它理解成“阻塞式 prepare”。
player.prepareSync()
当前就是 prepare() 的同义写法。
player.start()
作用
开始播放。
返回值
播放器对象本身。
player.pause()
作用
暂停播放。
返回值
播放器对象本身。
player.stop()
作用
停止播放。
返回值
播放器对象本身。
说明
它会顺手把当前“等待播放结束”的 latch 也释放掉,所以正在 awaitForCompletion() 的流程会结束。
player.seekTo(msec)
作用
跳转到指定毫秒位置,并阻塞等待 seek 完成。
返回值
播放器对象本身。
示例
player.seekTo(15000);
player.setScreenOnWhilePlaying(keep)
作用
播放期间是否保持屏幕常亮。
返回值
播放器对象本身。
player.awaitForCompletion()
作用
阻塞等待当前音频自然播放结束。
返回值
播放器对象本身。
什么时候适合用
适合“我后面的逻辑必须等这段音频播完再继续”。
示例
const player = new media.MediaPlayer();
player.play("/sdcard/Music/notify.mp3");
player.awaitForCompletion();
log("done");
player.release();
player.release()
作用
释放播放器资源。
返回值
播放器对象本身。
很重要
一旦 release() 之后,再去调 play()、setDataSource()、seekTo() 之类方法,会因为播放器已释放而报错:
MediaPlayer has already been released
建议
独立播放器不用了就尽快 release(),不要一直挂着。
只读状态属性
当前播放器对象还有几组很实用的状态属性。
player.duration
总时长,单位毫秒。
返回值
number
说明
读取失败时会回退成 0。
player.currentPosition
当前播放位置,单位毫秒。
返回值
number
player.isPlaying
当前是否在播放。
返回值
boolean
player.androidMediaPlayer
底层原生 android.media.MediaPlayer 对象。
什么时候会用到
只有当你明确知道自己要下沉到原生 Android API 时才需要它。普通脚本一般不必碰。
player.isReleased()
返回值
boolean
示例
if (!player.isReleased()) {
player.release();
}
一个完整例子:共享播放器做背景音乐
media.playMusic("/sdcard/Music/bgm.mp3", 0.4, true);
sleep(5000);
log(media.getMusicCurrentPosition());
media.pauseMusic();
sleep(1000);
media.resumeMusic();
sleep(2000);
media.stopMusic();
一个完整例子:独立播放器播完再继续
const player = new media.MediaPlayer();
try {
player
.setDataSource("/sdcard/Music/notify.mp3")
.setVolume(1)
.prepare()
.start()
.awaitForCompletion();
log("播放结束,继续后续逻辑");
} finally {
player.release();
}
一个完整例子:扫描新导出的音频文件
const out = "/sdcard/Download/demo.mp3";
// 这里假设你前面已经把文件写出来了
if (media.scanFile(out)) {
log("已通知系统重新扫描");
}
