【问题标题】:Garbage Collection causes : MediaPlayer finalized without being released垃圾收集原因:MediaPlayer 未发布就完成了
【发布时间】:2013-02-22 11:21:41
【问题描述】:

经过大量调试,我终于找到了导致此错误的原因!垃圾收集!

我在媒体视图中播放了一个视频,我正在后台寻找来自 Rest API 的新视频。

我时不时地看到垃圾收集在运行:

02-22 13:14:57.969: D/dalvikvm(16888): GC_EXPLICIT freed 152K, 4% free 6746K/6979K, paused 2ms+2ms

紧随其后的是:

02-22 13:14:57.969: W/MediaPlayer-JNI(16888): MediaPlayer finalized without being released

所以我每 5 秒调用一次 System.gc() 对其进行测试。

一旦第一次 GC 被调用,它就会发生!

02-22 13:19:47.813: D/dalvikvm(17060): GC_EXPLICIT freed 167K, 5% free 6745K/7047K, paused 2ms+2ms ---- I call GC
02-22 13:19:47.813: W/MediaPlayer-JNI(17060): MediaPlayer finalized without being released ---- VIDEO PLAY INTERRUPTED

为什么会这样?我可以预防吗?

播放视频:

private void playMedia(int playListIndex) throws IOException {
        File mediadir = getDir("tvr", Context.MODE_PRIVATE);
        filelist = mediadir.listFiles();
        Log.i("media player", "play media!");
        String path = filelist[playListIndex].getAbsolutePath();
        FileInputStream fileInputStream = new FileInputStream(path);
        final Uri uri = Uri.parse(path);
        String filename = filelist[playListIndex].getName();
        if (filename.contains("image")) {
            imageView = (ImageView)findViewById(R.id.imageView);
            imageView.setVisibility(View.VISIBLE);
            imageView.setImageURI(uri);
            mHandler.postDelayed(new Runnable() {
                public void run() {
                    imageView.setVisibility(View.GONE);
                    imageView.setImageURI(uri);
                    onCompletion(null);
                }
            }, 4000);
        } else if (filename.contains("video")) {

            MediaPlayer pl = new MediaPlayer();
            pl.setOnCompletionListener(this);
            pl.setDisplay(holder);
            pl.setDataSource(fileInputStream.getFD());
            pl.prepare();
            pl.start();
        }
    }

当它完成时:

@Override
    public void onCompletion(MediaPlayer mp) {
        Log.i("media player", "play next please!");
        if (mp != null) {
            mp.release();
        }
//      play next video
        currentMedia++;
        if (currentMedia > playList.size() - 1) {
            currentMedia = 0;
        }
        try {
            playMedia(currentMedia);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

【问题讨论】:

  • 能否请您展示一些您已实现媒体播放器的代码?

标签: android garbage-collection android-asynctask


【解决方案1】:

我认为这是因为您在方法范围内创建了媒体播放器,因此,当方法完成时,它超出了范围。这意味着没有引用,因此可以进行垃圾收集。

这意味着,它甚至可以在调用 onCompletion 之前被 GC 释放,因此在清除之前不会释放。相反,您需要将媒体播放器的引用作为成员变量存储在您的类中。

【讨论】:

  • 该死的!非常感谢!现在可以了,我知道这很愚蠢!
  • 我也遇到了同样的问题,虽然是开发者的疏忽造成的,但是当你进入某事的时候发现这些错误经常是有问题的;-) 谢谢!
  • 我在堆栈上读过的最好的答案之一,对 Java、范围和 Gc 非常有洞察力。谢谢你!
  • 非常感谢:)。是的,这是我的愚蠢错误。但这里的重点是,既然 MediaPlayer 绑定到应用程序的上下文,GC 不应该等待吗?
  • 好吧,如果我们使用 OnCompletionListener,MediaPlayer 对象会在回调中传回,那么为什么要对它进行垃圾回收呢?
【解决方案2】:

Java GC 只管理内存。所以当使用其他资源(例如文件、套接字等)时,您需要手动管理它们。对于 MediaPlayer,documentation 提到:

还建议一旦不再使用 MediaPlayer 对象,立即调用 release() 以便与 MediaPlayer 对象关联的内部播放器引擎使用的资源可以立即释放。资源可能包括单例资源,例如硬件加速组件,调用 release() 失败可能会导致 MediaPlayer 对象的后续实例回退到软件实现或完全失败。

因此,当您使用完 MediaPlayer 实例后,您需要确保在该实例上显式调用 release()。执行此操作的好地方可能是包含 Activity 的生命周期方法,例如销毁()。否则,当该 MediaPlayer 实例最终被垃圾回收时(在您不再引用它之后的某个任意时间),终结器会注意到您从未调用 release() 并将输出您看到的警告。

【讨论】:

  • 您好,谢谢!视频播放完毕后,我正在调用 release()
  • 不是,我在调用 release()。这甚至发生在第一个视频播放完毕之前,它发生在我调用 gc() 的前 5 秒内。
  • 现在有代码了,T. Kiley 有你的答案。创建 MediaPlayer 后,您不会保留对它的任何引用,因此它会被提前进行垃圾收集。
  • 我在哪里调用释放?如果我在 MediaPlayer.start() 之后立即调用它,它不会播放
  • Ruchir:如前所述,这样做的好地方是在活动的生命周期方法中,例如onStop() 或 onDestroy()。
猜你喜欢
  • 1970-01-01
  • 2021-11-19
  • 2014-01-31
  • 1970-01-01
  • 2012-09-17
  • 2021-03-25
  • 2014-07-24
  • 1970-01-01
  • 2013-12-03
相关资源
最近更新 更多