【问题标题】:"The surface has been released" inside "surfaceCreated"“surfaceCreated”里面的“surface has been released”
【发布时间】:2013-08-26 19:46:08
【问题描述】:

我知道这是一个常见问题,但是此堆栈跟踪显示其他问题。您可以看到,即使在 surfaceCreated 内部调用了 setDisplay(holder),它仍然会抛出 IllegalArgumentException。这也不是罕见的例外,昨天在约 3,000,000 次剪辑视图中发生了约 125,000 次。我可以向您保证,mCurrentPlayer 也已正确初始化。

表面创建:

@Override
public void surfaceCreated(SurfaceHolder holder) {
    mIsSurfaceCreated = true;
    mCurrentPlayer.setDisplay(holder);
}

表面销毁:

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    mIsSurfaceCreated = false;

    // Could be called after player was released in onDestroy.
    if (mCurrentPlayer != null) {
        mCurrentPlayer.setDisplay(null);
    }
}

堆栈跟踪:

java.lang.IllegalArgumentException: The surface has been released
    at android.media.MediaPlayer._setVideoSurface(Native Method)
    at android.media.MediaPlayer.setDisplay(MediaPlayer.java:660)
    at com.xxx.xxx.view.VideoPlayerView.surfaceCreated(VideoPlayerView.java:464)
    at android.view.SurfaceView.updateWindow(SurfaceView.java:543)
    at android.view.SurfaceView.access$000(SurfaceView.java:81)
    at android.view.SurfaceView$3.onPreDraw(SurfaceView.java:169)
    at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:590)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1644)
    at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2505)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:4945)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:511)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
    at dalvik.system.NativeStart.main(Native Method)

关于其他可能出现问题的任何想法? SurfaceHolder 是否可能会破坏后台线程上的表面,然后等待主线程(当前由 surfaceCreated 占用)完成它的块,然后才能在主线程上调用 surfaceDestroyed(我什至不认为锁可以修)?还有什么?

更新 -- 在深入挖掘之后,我发现了导致“表面已被释放”为 thrown 的原因:

哪些引用android_view_Surface_getSurface可以找到here

这是我缺乏 C++ 知识的痛处,它看起来像是试图锁定表面,如果不能,返回的表面将是 null。一旦返回为nullIllegalArgumentException 将被抛出。

【问题讨论】:

  • 您是否在后台线程上进行任何渲染?你的surfaceDestroyed的实现是什么? (请注意,surfaceDestroyed 的文档说:“如果您有一个直接访问表面的渲染线程,则必须确保该线程在从该函数返回之前不再接触表面。”。)
  • 没有背景渲染,Surface 仅供MediaPlayer 用于显示视频。一旦mp.setDisplay(surface) 被调用,我就不会接触表面。我也用 surfaceDestroyed 更新了我的帖子。注意根据android_media_MediaPlayer.cpp,如果jsurfacenull这个异常不会抛出。
  • 你能分享这个项目吗?问题很可能不在您显示的代码中,而是在应用程序的其他部分。
  • 手机从锁定状态恢复时是否可能出现问题?
  • 我过去在使用 Android VideoViews/MediaPlayers 时遇到过类似的问题。事实证明,底层的 SurfaceView 正在收集垃圾。我通过向 MediaPlayer 添加一个 onPreparedLister 来解决它,然后在我使用它时在我的类中持有对它的显式引用。也许这会有所帮助。

标签: android android-mediaplayer surfaceholder


【解决方案1】:

我刚刚解决了一个类似的问题。

我的调查显示,SurfaceView 中存在一个错误,导致将无效的表面传递给 surfaceCreated 回调方法。

这是修复它的 android repo 中的提交:link

似乎在 4.2 版本中引入了 android 源中的修复。而且,从应用程序的崩溃中,我看到无效表面导致的崩溃仅发生在 4.0 和 4.1 上。

所以,我可以假设在 4.0 之前,将无效表面传递给 MediaPlayer 是有效的。并且在 4.0 中 SurfaceView/MediaPlayer 的逻辑发生了变化,导致它不再有效。但是 SurfaceView 中的代码在 4.2 之前没有更新(其中修复了 SurfaceView 中的这个问题)。

我已经检查了 android 的 git repo,实际上,标记为 android-4.0.1_r1 的版本不包含修复,而标记为 android-4.2.1_r1 的版本包含它。

因此,要为不包含修复的平台修复它,仅在平台 4.0 及更高版本需要将其设置为 MediaPlayer 之前手动检查表面是否有效:

@Override public void surfaceCreated(final SurfaceHolder holder) {
    final Surface surface = holder.getSurface();

    if ( surface == null ) return;

    // on pre Ice Scream Sandwich (4.0) versions invalid surfaces seems to be accepted (or at least do not cause crash)
    final boolean invalidSurfaceAccepted = Build.VERSION.SDK_INT < Build.ICE_CREAM_SANDWICH;
    final boolean invalidSurface = ! surface.isValid();

    if ( invalidSurface && ( ! invalidSurfaceAccepted ) ) return;

    _mediaPlayer.setDisplay(holder);
}

这样,在旧平台上,无效表面将成功设置为媒体播放器并播放视频,在 4.0-4.1 平台上它将丢弃无效表面(我认为将使用有效表面再次调用 surfaceCreated)并且在 4.2 和更高版本上,surfaceCreated 将不会被无效的表面调用。

【讨论】:

  • 如果表面无效,您会采取什么措施? surfaceCreated 不会被再次调用,所以你只是删除 SurfaceView 并重新添加它,希望创建的 Surface 第二次有效?
  • 不幸的是,我无法重现这个问题,只有崩溃日志,所以我唯一能做的就是防止崩溃——我无法测试它在不同的“修复”情况下的表现。也许这些关于问题原因的信息对某人有用。
  • 我已经用更多细节和更有用的解决方案更新了我的答案。
  • 我的崩溃日志似乎反映了该修复是在 Android 版本 4.1.2 中进行的。我在 4.0.3、4.0.4 和 4.1.1 版本上发现了大约 250 万个异常,但是 4.1.2 和 4.1.2 上没有一个是我们应用程序使用最多的操作系统版本。但是,很好的调查,这些信息非常有价值!
  • invalidSurfaceAccepted 的值是多少?
【解决方案2】:

过去我在使用 Android VideoViews/MediaPlayers 时遇到过类似的问题。事实证明,底层的 SurfaceView 正在收集垃圾。我通过向 MediaPlayer 添加一个 onPreparedLister 来解决它,然后在我使用它时在我的类中持有对它的显式引用。也许这会有所帮助。

【讨论】:

  • 这实际上是为我解决的问题。为什么这些对象会被 GC 我不知道。
  • 很高兴所有这些小时的痛苦帮助了其他人!
  • 你可以知道你的努力帮助了不止一个人。这也是我的问题。谢谢。
猜你喜欢
  • 1970-01-01
  • 2012-09-19
  • 2022-08-20
  • 2018-09-21
  • 2019-07-23
  • 2013-05-04
  • 2020-03-13
  • 1970-01-01
相关资源
最近更新 更多