update:优化性能问题

This commit is contained in:
2026-08-17 10:11:28 +08:00
parent 2628a17b3f
commit 63fb4ae85a
15 changed files with 395 additions and 137 deletions
+121 -22
View File
@@ -112,6 +112,8 @@ export const audioFils = {
const AUDIO_LOAD_TIMEOUT_MS = 5000;
const AUDIO_WARM_CONCURRENCY = 3;
const AUDIO_WARM_RETRIES = 1;
const AUDIO_INSTANCE_LIMIT = 24;
const AUDIO_QUEUE_COMPACT_THRESHOLD = 12;
const AUDIO_WARM_PRIORITY_KEYS = [
"点击按钮",
"设备连接已断开",
@@ -162,6 +164,8 @@ class AudioManager {
this.loadingKeyPromises = new Map();
this.pendingPlayKeys = new Set();
this.cacheWritePromises = new Map();
this.audioLastUsedAt = new Map();
this.warmPriorityPromise = null;
this.warmAllPromise = null;
// 连续播放队列相关属性
@@ -253,14 +257,74 @@ class AudioManager {
}
getWarmupKeys() {
return Array.from(
new Set([...AUDIO_WARM_PRIORITY_KEYS, ...Object.keys(audioFils)])
);
return Array.from(new Set(AUDIO_WARM_PRIORITY_KEYS));
}
getAllAudioKeys() {
return Object.keys(audioFils);
}
touchAudioKey(key) {
this.audioLastUsedAt.set(key, Date.now());
}
destroyAudioInstance(key) {
const audio = this.audioMap.get(key);
if (!audio) return false;
this.clearPlayWatchdog(key);
this.audioMap.delete(key);
this.readyMap.delete(key);
this.allowPlayMap.delete(key);
this.audioLastUsedAt.delete(key);
try {
audio.destroy();
} catch (_) {}
// destroy 可能同步触发 onStop,最后再清一次对应状态。
this.allowPlayMap.delete(key);
return true;
}
pruneAudioInstances(preserveKey) {
while (this.audioMap.size >= AUDIO_INSTANCE_LIMIT) {
const candidate = Array.from(this.audioMap.keys())
.filter(
(key) =>
key !== preserveKey &&
key !== this.currentPlayingKey &&
!this.loadingKeyPromises.has(key)
)
.sort(
(left, right) =>
(this.audioLastUsedAt.get(left) || 0) -
(this.audioLastUsedAt.get(right) || 0)
)[0];
if (!candidate || !this.destroyAudioInstance(candidate)) return;
}
}
compactSequenceQueue(force = false) {
if (!this.isSequenceRunning || this.sequenceIndex <= 0) return;
if (!force && this.sequenceIndex < AUDIO_QUEUE_COMPACT_THRESHOLD) return;
this.sequenceQueue = this.sequenceQueue.slice(this.sequenceIndex);
this.sequenceIndex = 0;
}
getRuntimeStats() {
return {
audioInstanceCount: this.audioMap.size,
loadingAudioCount: this.loadingKeyPromises.size,
pendingPlayCount: this.pendingPlayKeys.size,
sequenceQueueLength: this.isSequenceRunning
? Math.max(this.sequenceQueue.length - this.sequenceIndex, 0)
: 0,
};
}
ensureAudio(key) {
if (!audioFils[key]) return Promise.resolve(false);
if (this.readyMap.get(key) && this.audioMap.has(key)) {
this.touchAudioKey(key);
return Promise.resolve(true);
}
@@ -312,23 +376,39 @@ class AudioManager {
return this.warmKeys(["点击按钮"], { concurrency: 1, retries: 1 });
}
warmAll() {
if (this.warmAllPromise) return this.warmAllPromise;
warmCommon() {
if (this.warmPriorityPromise) return this.warmPriorityPromise;
debugLog("开始后台分级预热全部语音");
debugLog("开始后台预热常用语音");
this.isLoading = true;
this.warmAllPromise = this.warmKeys(this.getWarmupKeys())
this.warmPriorityPromise = this.warmKeys(this.getWarmupKeys())
.catch((error) => {
debugLog("后台语音预热异常", error);
debugLog("常用语音预热异常", error);
})
.finally(() => {
this.isLoading = false;
debugLog("后台语音预热结束", this.getLoadProgress());
debugLog("常用语音预热结束", this.getRuntimeStats());
});
return this.warmPriorityPromise;
}
warmAll() {
if (this.warmAllPromise) return this.warmAllPromise;
debugLog("音频测试页开始遍历全部语音");
this.isLoading = true;
this.warmAllPromise = this.warmKeys(this.getAllAudioKeys())
.catch((error) => {
debugLog("全部语音遍历异常", error);
})
.finally(() => {
this.isLoading = false;
debugLog("全部语音遍历结束", this.getRuntimeStats());
});
return this.warmAllPromise;
}
// 保留旧入口兼容测试页或其他历史调用,实际行为改为非阻塞后台预热
// 音频测试页保留全量遍历;实例池会及时淘汰旧实例,避免同时常驻全部语音
initAudios() {
return this.warmAll();
}
@@ -366,6 +446,7 @@ class AudioManager {
return;
}
this.pruneAudioInstances(key);
const audio = uni.createInnerAudioContext();
audio.autoplay = false;
try {
@@ -377,6 +458,7 @@ class AudioManager {
} catch (_) {}
this.allowPlayMap.set(key, false);
audio.onPlay(() => {
if (this.audioMap.get(key) !== audio) return;
if (!this.allowPlayMap.get(key)) {
try {
audio.stop();
@@ -414,6 +496,7 @@ class AudioManager {
}
this.readyMap.set(key, false);
this.audioMap.delete(key);
this.audioLastUsedAt.delete(key);
try {
audio.destroy();
} catch (_) {}
@@ -446,6 +529,7 @@ class AudioManager {
if (!isCurrentGeneration()) {
clearLoadTimeout();
this.audioMap.delete(key);
this.audioLastUsedAt.delete(key);
try {
audio.destroy();
} catch (_) {}
@@ -480,6 +564,7 @@ class AudioManager {
});
audio.onEnded(() => {
if (this.audioMap.get(key) !== audio) return;
this.finishPlayback(key, {
advanceSequence: true,
emitEnded: true,
@@ -487,10 +572,12 @@ class AudioManager {
});
audio.onStop(() => {
if (this.audioMap.get(key) !== audio) return;
this.finishPlayback(key);
});
this.audioMap.set(key, audio);
this.touchAudioKey(key);
audio.src = realSrc;
};
@@ -700,14 +787,7 @@ class AudioManager {
const loadingPromise = this.loadingKeyPromises.get(key);
if (loadingPromise) return loadingPromise;
this.clearPlayWatchdog(key);
const oldAudio = this.audioMap.get(key);
if (oldAudio) {
try {
oldAudio.destroy();
} catch (_) {}
}
this.audioMap.delete(key);
this.destroyAudioInstance(key);
this.readyMap.set(key, false);
return this.ensureAudio(key);
}
@@ -754,7 +834,8 @@ class AudioManager {
// 不打断当前播放:把新的队列加入到序列中,等待当前播放结束后衔接
if (this.currentPlayingKey) {
if (this.isSequenceRunning) {
// 已有序列在跑:直接追加
// 已有序列在跑:先移除已消费前缀,再追加待播语音。
this.compactSequenceQueue(true);
this.sequenceQueue = this.sequenceQueue.concat(queue);
} else {
// 没有序列但当前有正在播放的:以当前为序列的起点
@@ -763,6 +844,17 @@ class AudioManager {
this.sequenceIndex = 0;
// 不触发 _playSingle,等待当前音频自然结束后由 onAudioEnded 接管
}
} else if (
this.isSequenceRunning &&
this.sequenceQueue[this.sequenceIndex]
) {
// 当前队首正在按需加载时只追加,不能覆盖已经排队的语音。
this.compactSequenceQueue(true);
this.sequenceQueue = this.sequenceQueue.concat(queue);
const pendingKey = this.sequenceQueue[this.sequenceIndex];
if (!this.pendingPlayKeys.has(pendingKey)) {
this._playSingle(pendingKey, false);
}
} else {
// 当前没有播放:直接启动新的序列
this.sequenceQueue = queue;
@@ -869,6 +961,7 @@ class AudioManager {
this.currentPlayingKey = key;
this.lastPlayKey = key;
this.lastPlayAt = Date.now();
this.touchAudioKey(key);
this.startPlayWatchdog(key);
} else {
debugLog(`音频 ${key} 尚未就绪,按需加载后播放...`);
@@ -885,7 +978,8 @@ class AudioManager {
const nextIndex = this.sequenceIndex + 1;
if (nextIndex < this.sequenceQueue.length) {
this.sequenceIndex = nextIndex;
const nextKey = this.sequenceQueue[nextIndex];
this.compactSequenceQueue();
const nextKey = this.sequenceQueue[this.sequenceIndex];
this._playSingle(nextKey, false);
} else {
// 队列播放完成
@@ -969,6 +1063,7 @@ class AudioManager {
reloadAll() {
debugLog("执行 reloadAll: 重置音频实例并后台恢复");
const shouldWarmAll = this.warmAllPromise !== null;
const shouldWarmPriority = this.warmPriorityPromise !== null;
this.loadGeneration += 1;
// 1. 停止所有播放
@@ -981,6 +1076,7 @@ class AudioManager {
} catch (_) {}
}
this.audioMap.clear();
this.audioLastUsedAt.clear();
// 3. 重置状态
this.readyMap.clear();
@@ -999,10 +1095,13 @@ class AudioManager {
// 4. 重置后台预热状态
this.isLoading = false;
this.warmPriorityPromise = null;
this.warmAllPromise = null;
// 5. 之前已启动过全量预热则继续全量恢复,否则只恢复按钮音效
return shouldWarmAll ? this.warmAll() : this.warmButton();
// 5. 按中断前使用范围恢复,产品页不再全量创建语音实例
if (shouldWarmAll) return this.warmAll();
if (shouldWarmPriority) return this.warmCommon();
return this.warmButton();
}
}