getBackgroundAudioManager在uni-app鸿蒙系统下onWaiting不会执行

getBackgroundAudioManager在uni-app鸿蒙系统下onWaiting不会执行

开发环境 版本号 项目创建方式
Windows win11 HBuilderX

产品分类:uniapp/App
PC开发环境操作系统:Windows
PC开发环境操作系统版本号:win11
HBuilderX类型:正式
HBuilderX版本号:4.76
手机系统:HarmonyOS NEXT
手机系统版本号:HarmonyOS 5.1.0
手机厂商:模拟器
手机机型:模拟器api19
页面类型:vue
vue版本:vue3
打包方式:云端
项目创建方式:HBuilderX

示例代码:

<template>  
    <view class="listen">  
        <view class="header">  
            <text class="title">Listening</text>  
            <view class="right">  
                <text class="book-icon">?</text>  
                <text class="link">绘本馆</text>  
            </view>  
        </view>  

        <image :src="cover" class="cover"></image>  
        <text class="book">{{ title }}</text>  

        <view class="slider-wrap">  
            <slider   
                :value="progress"   
                :disabled="totalSeconds===0"   
                @changing="onChanging"   
                @change="onSeek"   
                activeColor="#0E4B3B"   
                backgroundColor="#e0e0e0"  
                block-size="20"   
                step="1"  
                min="0"  
                max="100"  
            />  
            <view class="time-row">  
                <text class="t">{{ displayCurrentLabel }}</text>  
                <view class="rate-pill" @tap="changeRate">{{ rateLabel }}</view>  
                <text class="t">{{ displayDurationLabel }}</text>  
            </view>  
        </view>  

        <view class="controls">  
            <text class="ctrl" @tap="toggleShuffle">?</text>  
            <text class="ctrl" @tap="prev15">⏮️</text>  
            <view class="play" @tap="togglePlay">  
                <view v-if="showSpinner" class="loading-spinner"></view>  
                <text v-else class="play-icon">{{ playing ? '⏸️' : '▶️' }}</text>  
            </view>  
            <text class="ctrl" @tap="next15">⏭️</text>  
            <text class="ctrl" @tap="toggleLoop">?</text>  
        </view>  
    </view>  
</template>  

<script>  
    export default {  
        data(){  
            return {  
                cover: '/static/images/sample-cover.jpg',  
                title: 'Sample Story',  
                src: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3',  
                playing: false,  
                loading: false,  
                progress: 0,  
                currentLabel: '00:00',  
                durationLabel: '03:45',  
                totalSeconds: 225,  
                currentSeconds: 0,  
                loop: false,  
                rates: [0.75, 1, 1.5],  
                rateIdx: 1,  
                timer: null,  
                audioManager: null,  
                useRealAudio: true,  
                isHarmony: false,  
                supportRate: true,  
                isDragging: false,  
                previewSeconds: 0  
            }  
        },  
        computed:{  
            rate(){ return this.rates[this.rateIdx] },  
            rateLabel(){ return this.rate + 'x' },  
            displayCurrentLabel(){   
                if(this.isDragging) {  
                    return this.formatTime(this.previewSeconds) + ' ?'  
                }  
                if(this.loading) {  
                    return this.currentLabel + ' ⏳'  
                }  
                return this.currentLabel   
            },  
            displayDurationLabel(){   
                return this.formatTime(this.totalSeconds)   
            },  
            showSpinner() {  
                return this.loading  
            }  
        },  
        onLoad(query){  
            if(query && query.title){   
                this.title = decodeURIComponent(query.title)   
            }  
            console.log('熏听页面加载,标题:', this.title)  

            // 延迟初始化音频,避免启动时崩溃  
            setTimeout(() => {  
                this.initAudio()  
            }, 500)  
        },  
        onUnload() {  
            this.cleanup()  
        },  
        methods: {  
            initAudio() {  
                try {  
                    // 检测平台  
                    const sys = uni.getSystemInfoSync && uni.getSystemInfoSync()  
                    if((sys && /harmony/i.test(sys.osName||'')) || (typeof plus!=='undefined' && plus.os && /harmony/i.test(plus.os.name||''))){  
                        this.isHarmony = true  
                        this.supportRate = false  
                    }  

                    // 尝试创建背景音频管理器  
                    this.audioManager = uni.getBackgroundAudioManager()  
                    this.audioManager.title = this.title  
                    this.audioManager.src = this.src  

                    // 设置加载状态  
                    this.loading = true  

                    // 绑定音频事件  
                    this.audioManager.onWaiting(() => {  
                        console.log('音频缓冲中,设置loading状态')  
                        this.loading = true  
                        // loading状态会由onCanplay或onPlay事件清除  
                    })  

                    this.audioManager.onCanplay(() => {  
                        console.log('音频可以播放')  
                        this.loading = false  
                    })  

                    this.audioManager.onPlay(() => {  
                        console.log('音频开始播放')  
                        this.playing = true  
                        this.loading = false  
                        this.startProgressTimer()  
                    })  

                    this.audioManager.onPause(() => {  
                        console.log('音频暂停')  
                        this.playing = false  
                        this.loading = false  
                        this.clearTimer()  
                    })  

                    this.audioManager.onStop(() => {  
                        console.log('音频停止')  
                        this.playing = false  
                        this.loading = false  
                        this.progress = 0  
                        this.currentSeconds = 0  
                        this.updateDisplay()  
                        this.clearTimer()  
                    })  

                    this.audioManager.onEnded(() => {  
                        console.log('音频播放结束')  
                        this.loading = false  
                        if(this.loop) {  
                            this.loading = true  
                            this.audioManager.seek(0)  
                            this.audioManager.play()  
                        } else {  
                            this.playing = false  
                            this.clearTimer()  
                        }  
                    })  

                    this.audioManager.onTimeUpdate(() => {  
                        if(this.audioManager.duration > 0) {  
                            this.totalSeconds = this.audioManager.duration || 0  
                            this.currentSeconds = this.audioManager.currentTime || 0  
                            this.updateDisplay()  
                        }  
                    })  

                    this.audioManager.onError((e) => {  
                        console.warn('音频播放出错:', e)  
                        this.loading = false  
                        this.fallbackToSimulation()  
                    })  

                    console.log('音频管理器初始化成功')  

                } catch(e) {  
                    console.warn('音频管理器初始化失败,使用模拟播放:', e)  
                    this.loading = false  
                    this.fallbackToSimulation()  
                }  
            },  

            fallbackToSimulation() {  
                console.log('切换到模拟播放模式')  
                this.useRealAudio = false  
                this.audioManager = null  
            },  

            formatTime(seconds) {  
                if(!seconds || seconds < 0) return '00:00'  
                const mins = Math.floor(seconds / 60) || 0  
                const secs = Math.floor(seconds % 60) || 0  
                return `${mins.toString().padStart(2,'0')}:${secs.toString().padStart(2,'0')}`  
            },  

            updateDisplay() {  
                this.currentLabel = this.formatTime(this.currentSeconds || 0)  
                this.durationLabel = this.formatTime(this.totalSeconds || 0)  

                // 拖拽时不更新progress,避免冲突  
                if(!this.isDragging) {  
                    if(this.totalSeconds > 0) {  
                        this.progress = Math.floor((this.currentSeconds / this.totalSeconds) * 100) || 0  
                    } else {  
                        this.progress = 0  
                    }  
                }  
            },  

            startProgressTimer() {  
                this.clearTimer()  
                if(!this.useRealAudio) {  
                    this.timer = setInterval(() => {  
                        if(this.playing && this.currentSeconds < this.totalSeconds) {  
                            this.currentSeconds += this.rate  
                            this.updateDisplay()  
                        } else if(this.currentSeconds >= this.totalSeconds) {  
                            if(this.loop) {  
                                this.currentSeconds = 0  
                                this.updateDisplay()  
                            } else {  
                                this.playing = false  
                                this.clearTimer()  
                            }  
                        }  
                    }, 1000)  
                }  
            },  

            togglePlay(){   
                if(this.loading) {  
                    uni.showToast({   
                        title: '音频加载中,请稍候',   
                        icon: 'none'   
                    })  
                    return  
                }  

                if(this.useRealAudio && this.audioManager) {  
                    try {  
                        if(this.playing) {  
                            this.audioManager.pause()  
                        } else {  
                            this.loading = true  
                            this.audioManager.play()  
                        }  
                    } catch(e) {  
                        console.warn('音频操作失败,切换到模拟模式:', e)  
                        this.loading = false  
                        this.fallbackToSimulation()  
                        this.togglePlaySimulation()  
                    }  
                } else {  
                    this.togglePlaySimulation()  
                }  
            },  

            togglePlaySimulation() {  
                this.playing = !this.playing  
                uni.showToast({   
                    title: this.playing ? '开始播放(模拟)' : '暂停播放',   
                    icon: 'none'   
                })  

                if(this.playing) {  
                    this.startProgressTimer()  
                } else {  
                    this.clearTimer()  
                }  
            },  

            clearTimer() {  
                if(this.timer) {  
                    clearInterval(this.timer)  
                    this.timer = null  
                }  
            },  

            onChanging(e) {  
                console.log('滑块拖拽中:', e.detail.value)  
                this.isDragging = true  
                const value = Number(e.detail.value) || 0  

                // 计算预览时间  
                if(this.totalSeconds > 0) {  
                    this.previewSeconds = Math.max(0, (this.totalSeconds * value) / 100)  
                } else {  
                    this.previewSeconds = 0  
                }  

                // 注意:不需要手动设置progress,slider组件会自动处理  
                console.log('预览时间:', this.previewSeconds, '秒')  
            },  

            onSeek(e) {  
                console.log('滑块拖拽结束:', e.detail.value)  

                const value = Number(e.detail.value) || 0  

                if(this.totalSeconds <= 0) {  
                    this.isDragging = false  
                    return  
                }  

                const targetSeconds = Math.max(0, (this.totalSeconds * value) / 100)  
                console.log('跳转到目标时间:', targetSeconds, '秒')  

                // 立即更新currentSeconds,确保界面同步  
                this.currentSeconds = targetSeconds  

                if(this.useRealAudio && this.audioManager) {  
                    try {  
                        console.log('真实音频跳转')  
                        this.loading = true  
                        this.audioManager.seek(targetSeconds)  
                    } catch(e) {  
                        console.warn('音频跳转失败:', e)  
                    }  
                } else {  
                    console.log('模拟音频跳转')  
                    // 模拟音频直接更新  
                }  

                // 更新显示并清除拖拽状态  
                this.updateDisplay()  
                this.isDragging = false  
            },  

            prev15(){   
                const newTime = Math.max(0, (this.currentSeconds || 0) - 15)  

                if(this.useRealAudio && this.audioManager) {  
                    try {  
                        this.audioManager.seek(newTime)  
                    } catch(e) {  
                        console.warn('跳转失败:', e)  
                        this.currentSeconds = newTime  
                        this.updateDisplay()  
                    }  
                } else {  
                    this.currentSeconds = newTime  
                    this.updateDisplay()  
                }  

                uni.showToast({ title: '后退15秒', icon: 'none' })  
            },  

            next15(){   
                const newTime = Math.min((this.totalSeconds || 0), (this.currentSeconds || 0) + 15)  

                if(this.useRealAudio && this.audioManager) {  
                    try {  
                        this.audioManager.seek(newTime)  
                    } catch(e) {  
                        console.warn('跳转失败:', e)  
                        this.currentSeconds = newTime  
                        this.updateDisplay()  
                    }  
                } else {  
                    this.currentSeconds = newTime  
                    this.updateDisplay()  
                }  

                uni.showToast({ title: '快进15秒', icon: 'none' })  
            },  

            changeRate(){   
                if(!this.supportRate) {  
                    uni.showToast({ title:'当前平台不支持倍速', icon:'none' })  
                    return  
                }  

                this.rateIdx = (this.rateIdx + 1) % this.rates.length  

                if(this.useRealAudio && this.audioManager) {  
                    try {  
                        this.audioManager.playbackRate = this.rate  
                    } catch(e) {  
                        console.warn('设置倍速失败:', e)  
                        this.supportRate = false  
                    }  
                }  

                uni.showToast({ title: '倍速 ' + this.rate + 'x', icon:'none' })   
            },  

            toggleLoop(){   
                this.loop = !this.loop  
                uni.showToast({ title: this.loop ? '循环播放' : '关闭循环', icon:'none' })   
            },  

            toggleShuffle(){   
                uni.showToast({ title:'随机播放(占位)', icon:'none' })   
            },  

            cleanup() {  
                this.clearTimer()  
                if(this.audioManager) {  
                    try {  
                        this.audioManager.stop()  
                    } catch(e) {  
                        console.warn('停止音频失败:', e)  
                    }  
                }  
            }  
        }  
    }  
</script>  

<style scoped>  
    .listen{  
        padding: 12rpx;   
        background: #f5f7f9;   
        height: 100vh;   
        display: flex;   
        flex-direction: column;   
        justify-content: space-between;   
        box-sizing: border-box;  
    }  

    .header{   
        display: flex;   
        justify-content: space-between;   
        align-items: center;   
        margin-bottom: 6rpx;   
        flex-shrink: 0;   
    }  

    .title{   
        font-size: 40rpx;   
        font-weight: 800;   
        color: #333;   
    }  

    .right{   
        display: flex;   
        align-items: center;   
        color: #0E4B3B;   
    }  

    .book-icon{   
        margin-right: 6rpx;   
    }  

    .link{   
        color: #0E4B3B;   
    }  

    .cover{   
        width: 280rpx;   
        height: 320rpx;   
        border-radius: 8rpx;   
        display: block;   
        margin: 12rpx auto 6rpx;   
        flex-shrink: 0;   
    }  

    .book{   
        text-align: center;   
        font-size: 24rpx;   
        font-weight: 700;   
        color: #333;   
        margin-bottom: 12rpx;   
        flex-shrink: 0;   
    }  

    .slider-wrap{   
        padding: 0 16rpx;   
        margin: 6rpx 0;   
        flex-shrink: 0;  
    }  

    .time-row{   
        display: flex;   
        justify-content: space-between;   
        align-items: center;   
        color: #7a7f7d;   
        font-size: 20rpx;   
        margin-top: 4rpx;   
    }  

    .t{   
        min-width: 70rpx;   
        text-align: center;   
    }  

    .rate-pill{   
        padding: 4rpx 8rpx;   
        border-radius: 12rpx;   
        background: #e8f3ef;   
        color: #0E4B3B;   
        font-size: 18rpx;   
    }  

    .controls{   
        display: flex;   
        justify-content: space-around;   
        align-items: center;   
        margin: 12rpx 0;   
        flex-shrink: 0;   
    }  

    .ctrl{   
        font-size: 32rpx;   
        color: #0E4B3B;   
    }  

    .play{   
        width: 100rpx;   
        height: 100rpx;   
        border-radius: 50rpx;   
        background: #0E4B3B;   
        display: flex;   
        align-items: center;   
        justify-content: center;  
    }  

    .play-icon{   
        color: white;   
        font-size: 40rpx;   
    }  

    .loading-spinner {  
        width: 40rpx;  
        height: 40rpx;  
        border: 4rpx solid rgba(255, 255, 255, 0.3);  
        border-top: 4rpx solid white;  
        border-radius: 50%;  
        animation: spin 1s linear infinite;  
    }  

    @keyframes spin {  
        0% { transform: rotate(0deg); }  
        100% { transform: rotate(360deg); }  
    }  
</style>

更多关于getBackgroundAudioManager在uni-app鸿蒙系统下onWaiting不会执行的实战教程也可以访问 https://www.itying.com/category-93-b0.html

8 回复

经我测试,鸿蒙模拟器上有 loading 图标

更多关于getBackgroundAudioManager在uni-app鸿蒙系统下onWaiting不会执行的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


这个loading是一开始默认的loading,加载完成loading会取消,之后要拖动进度条,这时候就不会出现loading了

bug 已确认,已加分,预计 4.84-alpha 修复(如果中途有紧急更新会延后)

4.83没有修复这个bug吗

回复 1***@qq.com: 没有,4.83 是一个紧急更新,需要等下一个更新,着急的话我可以给你一个临时解决方案

回复 DCloud_UNI_LXH: 请问临时解决方案是什么,现在我们碰到同样的问题,只能播放一次,在播放其他音频时无法播放。

回复 g***@qq.com: 下载这个 har 包,放到项目根目录的 harmony-configs/libs 目录下 har 包链接

在鸿蒙系统下,getBackgroundAudioManageronWaiting事件确实可能存在兼容性问题。从你的代码分析,问题可能出现在以下几个方面:

  1. 鸿蒙系统音频事件触发机制差异:鸿蒙系统对音频缓冲状态的处理可能与Android/iOS不同,onWaiting事件可能未被正确触发

  2. 事件绑定时机问题:建议在音频管理器创建后立即绑定事件监听器:

this.audioManager = uni.getBackgroundAudioManager()
// 立即绑定事件
this.audioManager.onWaiting(() => {
    console.log('onWaiting triggered')
    this.loading = true
})
// 再设置其他属性
this.audioManager.title = this.title
this.audioManager.src = this.src
  1. 替代方案:可以结合onCanplayonTimeUpdate来间接检测缓冲状态:
this.audioManager.onTimeUpdate(() => {
    // 检测当前时间是否长时间不更新来判断缓冲
    if (this.playing && this.audioManager.currentTime === this.lastUpdateTime) {
        this.loading = true
    }
    this.lastUpdateTime = this.audioManager.currentTime
})
  1. 平台特定处理:针对鸿蒙系统添加特殊处理:
if (this.isHarmony) {
    // 鸿蒙系统使用备用方案
    this.setupHarmonyFallback()
}
回到顶部