如何在Android MediaPlayer中进行内存管理?

huangapple go评论78阅读模式
英文:

How to memory management in Android MediaPlayer?

问题

我使用了 Android 4.4.4 版本我的应用程序使用了 __MediaPlayer__

我已经在设备中获取了媒体列表并将每个媒体放入了 __mutableListOf<MediaPlayer>__接下来for 循环中播放这些媒体

但是经过一段时间后应用程序崩溃了

```java
10-18 12:58:29.599 1466-1466/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.hanmedia.android, PID: 1466
    java.lang.OutOfMemoryError
        ...
        ...
        ...
        at dalvik.system.NativeStart.main(Native Method)

我猜测是 MediaPlayer 使用了大量内存。并且在播放每个媒体时,Android 无法清除缓冲区。如何在此代码中清除缓冲区?

// class NormalFragment : Fragment() 代码

// 类型是 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()

// 然后循环播放每个视频
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 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
        }
    }
英文:

I used Android 4.4.4 version. And My application used MediaPlayer.

I have get media list in device and each media put in mutableListOf<MediaPlayer>. Next In the for loop that medias played.

But, It was dead after some time.

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.&lt;init&gt;(ProgressBar.java:294)
        at android.widget.ProgressBar.&lt;init&gt;(ProgressBar.java:246)
        at android.widget.AbsSeekBar.&lt;init&gt;(AbsSeekBar.java:69)
        at android.widget.SeekBar.&lt;init&gt;(SeekBar.java:83)
        at androidx.appcompat.widget.AppCompatSeekBar.&lt;init&gt;(AppCompatSeekBar.java:50)
        at androidx.appcompat.widget.AppCompatSeekBar.&lt;init&gt;(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)

I guess MediaPlayer use many memory. And Android can't clear buffer when play each media. How to clear buffer in this code?

// class NormalFragment : Fragment() Code

// Type is mutableListOf&lt;MediaItemPlayer&gt;
val playerList = device.mediaLinks
                .filter { link -&gt;
                    link.mediaLinkType == type &amp;&amp;
                            nowKr.time &lt; link.expire
                }
                .sortedBy {
                    it.position
                }
                .let { links -&gt;
                    val playerList = mutableListOf&lt;MediaItemPlayer&gt;()
                    links.forEachIndexed { index, _ -&gt;
                        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 -&gt;

                    val listener = if (index != (playerList.count() - 1)) {
                        object : PlayerEventListener {
                            override fun next() {
                                it.runOnUiThread {
                                    logger.i(&quot;[${type}] $index&quot;)
                                    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(&quot;ready [${mediaLink.mediaLinkType}]${mediaLink.media.originalName}&quot;)
            when (mediaLink.media.mediaType) {
                MediaType.TEXT, MediaType.PICTURE -&gt; {
                    val delayTime = mediaLink.waitTime * (mediaLink.repeatCount + 1)
                    logger.i(&quot;[${mediaLink.mediaLinkType}] delay $delayTime&quot;)
                    view.postDelayed({
                        goNext()
                    }, delayTime)
                }
                MediaType.VIDEO -&gt; {
                    logger.i(&quot;[${mediaLink.mediaLinkType}] Video start&quot;)
                    if(prepareFlag){
                        player.start()
                    }else{
                        view.postDelayed({
                            if(!prepareFlag) {
                                logger.e(&quot;[${mediaLink.mediaLinkType}] not prepare&quot;)
                                startIgnoreFlag = true
                                logger.e(&quot;[${mediaLink.mediaLinkType}] start ignore and play next video&quot;)
                                nextListener.next()
                            }
                        }, 2000)
                        startFlag = true
                    }
                }
            }
            context.runOnUiThread {
                view.visibility = View.VISIBLE
            }
        }else{
            logger.i(&quot;not ready [${mediaLink.mediaLinkType}]${mediaLink.media.originalName}&quot;)
            goNext()
        }
    }

    private fun goNext(){
        nextListener.next()
        context.runOnUiThread {
            view.visibility = View.GONE
        }
    }

答案1

得分: 1

我认为问题在于您创建了多个 MediaPlayer 实例,这导致了 OutOfMemoryError 异常。另外,在播放完成后不要忘记释放 MediaPlayer 实例。

根据 MediaPlayer 文档:

> 同样建议一旦不再使用 MediaPlayer 对象,立即调用 release() 方法,以便立即释放与 MediaPlayer 对象关联的内部播放器引擎使用的资源。资源可能包括诸如硬件加速组件之类的单例资源,不调用 release() 可能会导致后续的 MediaPlayer 对象实例回退到软件实现,或者完全失败。一旦 MediaPlayer 对象处于结束状态,就不能再使用它,也无法使其返回到任何其他状态。

在 MediaItemPlayer 中添加一个方法,在使用后释放 MediaPlayer:

private fun releaseMediaPlayer(){
   if (player.isPlaying()) {
       player.stop();
   }
   player.reset();
   player.release();
   player = null;
}

然后在 NormalFragment 中,在播放完媒体文件后调用 releaseMediaPlayer 方法。

英文:

I think the problem is that you are creating mutiple instances of MediaPlayer which is causing OutOfMemoryError exception. Also, do not forget to release MediaPlayer instance when you are done with playback.
According to MediaPlayer Documentation:

> It is also recommended that once a MediaPlayer object is no longer being used, call release() immediately so that resources used by the internal player engine associated with the MediaPlayer object can be released immediately. Resource may include singleton resources such as hardware acceleration components and failure to call release() may cause subsequent instances of MediaPlayer objects to fallback to software implementations or fail altogether. Once the MediaPlayer object is in the End state, it can no longer be used and there is no way to bring it back to any other state.

Add a method in MediaItemPlayer to release MediaPlayer after use:

    private fun releaseMediaPlayer(){
       if (player.isPlaying()) {
           player.stop();
       }
       player.reset();
       player.release();
       player = null;
}

Then call releaseMediaPlayer from NormalFragment after you are done playing a media file.

huangapple
  • 本文由 发表于 2020年10月18日 17:15:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/64411596.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定