java - 如何在 Android MediaPlayer 中进行内存管理?
问题描述
我使用的是安卓 4.4.4 版本。我的应用程序使用了 MediaPlayer。
我在设备中获得了媒体列表,并且每个媒体都放入了mutableListOf。Next 在媒体播放的 for 循环中。
但是,过了一段时间,它就死了。
10-18 12:58:29.599 1466-1466/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hanmedia.android, PID: 1466
java.lang.OutOfMemoryError
at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:594)
at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:429)
at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:840)
at android.content.res.Resources.loadDrawable(Resources.java:2110)
at android.content.res.Resources.getDrawable(Resources.java:700)
at android.graphics.drawable.AnimationDrawable.inflate(AnimationDrawable.java:282)
at android.graphics.drawable.Drawable.createFromXmlInner(Drawable.java:937)
at android.graphics.drawable.Drawable.createFromXml(Drawable.java:877)
at android.content.res.Resources.loadDrawable(Resources.java:2092)
at android.content.res.TypedArray.getDrawable(TypedArray.java:602)
at android.widget.ProgressBar.<init>(ProgressBar.java:294)
at android.widget.ProgressBar.<init>(ProgressBar.java:246)
at android.widget.AbsSeekBar.<init>(AbsSeekBar.java:69)
at android.widget.SeekBar.<init>(SeekBar.java:83)
at androidx.appcompat.widget.AppCompatSeekBar.<init>(AppCompatSeekBar.java:50)
at androidx.appcompat.widget.AppCompatSeekBar.<init>(AppCompatSeekBar.java:45)
at androidx.appcompat.app.AppCompatViewInflater.createSeekBar(AppCompatViewInflater.java:256)
at androidx.appcompat.app.AppCompatViewInflater.createView(AppCompatViewInflater.java:163)
at androidx.appcompat.app.AppCompatDelegateImpl.createView(AppCompatDelegateImpl.java:1551)
at androidx.appcompat.app.AppCompatDelegateImpl.onCreateView(AppCompatDelegateImpl.java:1602)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:684)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:758)
at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
at android.widget.MediaController.makeControllerView(MediaController.java:244)
at android.widget.MediaController.setAnchorView(MediaController.java:232)
at android.widget.VideoView.attachMediaController(VideoView.java:380)
at android.widget.VideoView.openVideo(VideoView.java:349)
at android.widget.VideoView.access$2100(VideoView.java:71)
at android.widget.VideoView$7.surfaceCreated(VideoView.java:607)
at android.view.SurfaceView.updateWindow(SurfaceView.java:572)
at android.view.SurfaceView.access$000(SurfaceView.java:86)
at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:175)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1876)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1001)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5680)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
at android.view.Choreographer.doFrame(Choreographer.java:544)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5001)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:815)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:631)
at dalvik.system.NativeStart.main(Native Method)
我猜MediaPlayer使用了很多内存。并且Android在播放每个媒体时无法清除缓冲区。如何清除此代码中的缓冲区?
// class NormalFragment : Fragment() Code
// Type is mutableListOf<MediaItemPlayer>
val playerList = device.mediaLinks
.filter { link ->
link.mediaLinkType == type &&
nowKr.time < link.expire
}
.sortedBy {
it.position
}
.let { links ->
val playerList = mutableListOf<MediaItemPlayer>()
links.forEachIndexed { index, _ ->
links[index].waitTime = device.pictureSlideTime.toLong()
links[index].sound = sound
it.runOnUiThread {
val player = MediaItemPlayer(it, links[index])
view.addView(player.view)
playerList.add(player)
}
}
playerList
}.toList()
// And then loop each video
playerList.forEachIndexed { index, mediaItemPlayer ->
val listener = if (index != (playerList.count() - 1)) {
object : PlayerEventListener {
override fun next() {
it.runOnUiThread {
logger.i("[${type}] $index")
val playIndex = (index + 1) % playerList.size
val prepareIndex = (index + 2) % playerList.size
playerList[playIndex].run {
setVolume(getSound(type))
start()
}
playerList[prepareIndex].run{
prepare()
}
}
}
// MediaItemPlayer class
// class MediaItemPlayer(private val context : Context, private val mediaLink : DeviceWithMediaLink)
private var repeatCount = 0
private var ready : Boolean = false
private var startFlag = false
private var startIgnoreFlag = false
private var prepareFlag = false
private lateinit var player : MediaPlayer
private lateinit var nextListener: PlayerEventListener
fun start(){
if(ready) {
logger.i("ready [${mediaLink.mediaLinkType}]${mediaLink.media.originalName}")
when (mediaLink.media.mediaType) {
MediaType.TEXT, MediaType.PICTURE -> {
val delayTime = mediaLink.waitTime * (mediaLink.repeatCount + 1)
logger.i("[${mediaLink.mediaLinkType}] delay $delayTime")
view.postDelayed({
goNext()
}, delayTime)
}
MediaType.VIDEO -> {
logger.i("[${mediaLink.mediaLinkType}] Video start")
if(prepareFlag){
player.start()
}else{
view.postDelayed({
if(!prepareFlag) {
logger.e("[${mediaLink.mediaLinkType}] not prepare")
startIgnoreFlag = true
logger.e("[${mediaLink.mediaLinkType}] start ignore and play next video")
nextListener.next()
}
}, 2000)
startFlag = true
}
}
}
context.runOnUiThread {
view.visibility = View.VISIBLE
}
}else{
logger.i("not ready [${mediaLink.mediaLinkType}]${mediaLink.media.originalName}")
goNext()
}
}
private fun goNext(){
nextListener.next()
context.runOnUiThread {
view.visibility = View.GONE
}
}
解决方案
我认为问题在于您正在创建导致 OutOfMemoryError 异常的 MediaPlayer 的多个实例。此外,不要忘记在播放完成后释放 MediaPlayer 实例。根据 MediaPlayer 文档:
还建议一旦不再使用 MediaPlayer 对象,立即调用 release() 以便与 MediaPlayer 对象关联的内部播放器引擎使用的资源可以立即释放。资源可能包括单例资源,例如硬件加速组件,调用 release() 失败可能会导致 MediaPlayer 对象的后续实例回退到软件实现或完全失败。一旦 MediaPlayer 对象处于 End 状态,就不能再使用它,也无法将其恢复到任何其他状态。
在MediaItemPlayer中添加一个方法,使用后释放MediaPlayer:
private fun releaseMediaPlayer(){
if (player.isPlaying()) {
player.stop();
}
player.reset();
player.release();
player = null;
}
播放完媒体文件后,从 NormalFragment 调用 releaseMediaPlayer。
推荐阅读
- c++ - 关于使用 ifstream 判断 C++ 中是否存在文件的问题
- java - 列表视图问题:尝试在空对象引用上调用虚拟方法 'void android.widget.ListView.setAdapter(android.widget.ListAdapter)'
- laravel - 使用默认类别 belongsToMany 创建新帖子
- parsing - 从 .txt 文件中解析文本
- mongodb - 使用 mongodb 和 monstache 进行慢速弹性搜索索引
- angularjs - 尝试创建服务来构建我的 NgTable
- ruby - 删除方法单一!数组中的元素按谓词并返回已删除
- javascript - Vue Nuxt Axios:带代理的跨域 POST
- javascript - 从 android 调用 Native 模块并将事件从该模块发送到 React Native
- html - 使垂直对齐的父块随着孩子的成长而向右成长的最简单方法是什么?