uni-app 使用video循环播放视频内存溢出
uni-app 使用video循环播放视频内存溢出
开发环境 | 版本号 | 项目创建方式 |
---|---|---|
Windows | windows 11 | HBuilderX |
产品分类:uniapp/App
PC开发环境操作系统:Windows
HBuilderX类型:正式
HBuilderX版本号:4.36
手机系统:Android
手机系统版本号:Android 5.1
手机厂商:华为
手机机型:工业机主板
页面类型:vue
vue版本:vue3
打包方式:云端
项目创建方式:HBuilderX
示例代码:
<template>
<view>
<video id="pageVideo" @ended="handleVideoEnd" @error="handleVideoError" :src="videoUrl" autoplay="true"
controls="false" :show-play-btn="false" :show-center-play-btn="false" :enable-play-gesture="false"
page-gesture="false" codec="software" v-if="showVideo">
<cover-view class="cover-container">
<cover-image :src="imageUrl" :id='pageImageId'></cover-image>
<cover-image class="uniLogo1" src="/static/logo.png" @click="logoClick"></cover-image>
<cover-image class="uniLogo2" src="/static/machineLogo.jpg" @click="machineLogoClick"></cover-image>
</cover-view>
</video>
<view>
<text>内容描述:{{dynamicText}}</text>
</view>
<view>
<text>点击的图片:{{imageClick}}</text>
</view>
<view>
<text>我是信息提示:{{showTips}}</text>
</view>
<view>
<text>最大内存:{{maxMemory}},其它内存信息{{memoryInfo}}</text>
</view>
</view>
</template>
<script>
export default {
data() {
return {
showVideo: true,
showTips: '',
dynamicText: '',
imageClick: '',
myArray: [],
startIndex: 0,
videoUrl: '',
imageUrl: '',
videoCtx: null, // 用于保存video上下文,
timeSpan: 3000,
pageImageId: '',
videoKey: false,
gcCount: 0,
clearTimer: null,
memoryTimer: null,
memoryInfo: '',
maxMemory: 0,
},
},
onLoad() {},
onReady() {
this.myArray = [
'/static/2.mp4',
];
var length = this.myArray.length;
//从视频数组中取最后一个,初始化后,播放视频时,从数组里第一个开始播
this.startIndex = length - 1;
this.dynamicText = "myArray里放了" + length + "个视频,3秒后开始播放视频";
this.playVideo();
},
methods: {
getMemoryInfo() {
this.memoryTimer = setInterval(() => {
if (plus.os.name === 'Android') {
const Runtime = plus.android.importClass('java.lang.Runtime');
const runtime = Runtime.getRuntime();
const usedMB = ((runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024)).toFixed((2));
if (usedMB > this.maxMemory) {
this.maxMemory = usedMB;
}
this.memoryInfo = "[内存监控] 使用中: " + usedMB + "MB";
}
}, 1000);
},
startTimer() {
this.clearTimer = setInterval(() => {
//执行GC
this.clearMem();
}, 10000);
},
stopTimer() {
if (this.clearTimer) {
clearInterval(this.clearTimer);
this.clearTimer = null;
}
if (this.memoryTimer) {
clearInterval(this.memoryTimer);
this.memoryTimer = null;
}
},
// 初始化video上下文
initVideoContext() {
if (!this.videoCtx) {
this.videoCtx = uni.createVideoContext('pageVideo', this);
}
},
logoClick() {
this.imageClick = '绿色绿色绿色绿色绿色绿色绿色的图片';
},
machineLogoClick() {
this.imageClick = '白色白色白色白色白色白色白色的图片';
},
//视频播放
playVideo() {
var url = this.myArray[0];
this.dynamicText = "现在播放的视频index是" + this.startIndex;
this.pageImageId = 'pageImageIdHidden';
this.showTips = '现在播放的是视频:' + url;
this.initVideoContext();
setTimeout(() => {
this.showVideo = true; // 重建组件(强制释放内存)
this.videoUrl = url;
this.videoCtx.play();
this.startIndex = this.startIndex + 1;
}, 100);
// 执行GC
this.clearMem();
},
clearMem() {
// 安卓原生代码(需封装为UniPlugin)
if (plus.os.name === 'Android') {
const System = plus.android.importClass('java.lang.System');
plus.android.invoke(this.videoCtx, 'release');
System.gc();
System.runFinalization();
if (plus.os.name === 'Android') {
const Runtime = plus.android.importClass('java.lang.Runtime');
const runtime = Runtime.getRuntime();
const usedMB = ((runtime.totalMemory() - runtime.freeMemory()) / (1024 * 1024)).toFixed((2));
this.gcCount += 1;
this.imageClick = "执行了GC" + this.gcCount + ',后内存大小为' + usedMB + "MB";
}
}
},
handleVideoEnd() {
try {
this.dynamicText = '视频正常播放结束,3秒后开始下一个视频';
this.videoCtx.pause();
this.videoCtx.stop();
this.videoCtx.seek(0);
this.videoUrl = ''; // 清除数据绑定
this.videoCtx = null;
// 先销毁再重新创建
this.showVideo = false;
this.clearMem();
setTimeout(() => {
this.$nextTick(() => {
this.playVideo();
});
}, 3000);
} catch (e) {
this.dynamicText = e;
}
},
handleVideoError(e) {},
}
}
</script>
<style>
/* 页面样式 */
page {
hardware-accelerated: false;
}
.cover-container {
position: relative;
width: 100%;
height: 100%;
}
.uniLogo1 {
position: absolute;
width: 120rpx;
height: 60rpx;
bottom: 10%;
left: 10%;
}
.uniLogo2 {
position: absolute;
width: 120rpx;
height: 60rpx;
bottom: 10%;
right: 10%;
}
#pageVideo {
width: 100%;
height: 100px;
}
#pageImageId {
position: absolute;
width: 100%;
height: 100%;
top: 0%;
left: 0%;
}
#pageImageIdHidden {
position: absolute;
width: 100%;
height: 100%;
top: 0%;
left: 0%;
display: none;
}
</style>
操作步骤:
- 循环播放本地视频(测试时使用的同一个视频)。
- 在 ended 内 秒执行一次内存抛出,详细见代码示例内的
clearMem()
;中间执行了v-if
。抛出内存后3秒执行循环播放。设置了runmode
为liberate
。 - 抛出内存后,监控显示内存被释放,再次播放视频后,占用的内存峰值要大于前一次内存峰值。
预期结果:
循环播放同一个视频时,抛出内存后,内存占用峰值不应该越来越高。
实际结果:
抛出内存后,监控显示内存被释放,再次播放视频后,占用的内存峰值要大于前一次内存峰值。
bug描述:
android 5.1.1,循环播放本地视频,抛出内存,执行 v-if
,设置 runmode
为 liberate
。应用程序内存一直增加。
单次播放视频后停止所有执行动作,内存占用几乎不变,继续播放视频,内存占用会越来越大。
本地测试,第一次执行最高占用大概是126,GC后的内存是12左右。每次播放完执行GC计数加1,执行175次后,占用内存为195.97或者更高,GC的涨到44.73。
第一次写 uniapp 的项目,不太清除是不是代码逻辑上出的问题。
更多关于uni-app 使用video循环播放视频内存溢出的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于uni-app 使用video循环播放视频内存溢出的实战教程也可以访问 https://www.itying.com/category-93-b0.html
针对uni-app循环播放视频内存溢出的问题,分析如下:
- 主要问题点:
- 视频组件销毁重建时内存未完全释放
- Android 5.1系统对视频解码的内存管理存在缺陷
- 频繁GC反而可能加剧内存碎片化
- 关键优化建议:
(1) 修改视频播放逻辑:
handleVideoEnd() {
this.videoCtx.stop();
this.videoCtx.seek(0);
setTimeout(() => {
this.videoUrl = ''; // 先清空src
this.$nextTick(() => {
this.videoUrl = this.myArray[0]; // 重新赋值
this.videoCtx.play();
});
}, 100);
}