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

How to memory management in Android MediaPlayer?

我用的是Android4.4.4版本。我的应用程序使用 MediaPlayer.

我在设备中获取了媒体列表,每个媒体都放入了 mutableListOf。 Next 在medias播放的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.access00(VideoView.java:71)
        at android.widget.VideoView.surfaceCreated(VideoView.java:607)
        at android.view.SurfaceView.updateWindow(SurfaceView.java:572)
        at android.view.SurfaceView.access[=11=]0(SurfaceView.java:86)
        at android.view.SurfaceView.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
        }
    }

我认为问题在于您正在创建 MediaPlayer 的多个实例,这会导致 OutOfMemoryError 异常。另外,不要忘记在完成播放后释放 MediaPlayer 实例。 根据 MediaPlayer 文档:

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.

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

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

然后在播放完媒体文件后从 NormalFragment 调用 releaseMediaPlayer。