英文:
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.<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)
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<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
}
}
答案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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论