10

我知道这是一个常见问题,但是此堆栈跟踪显示其他问题。你可以看到,即使setDisplay(holder)在里面被调用,surfaceCreated它仍然会抛出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主线程(我什至认为锁无法修复)?还有什么?

更新——深入研究后,我发现了导致“表面已被释放”的原因

android_view_Surface_getSurface可以在这里找到哪些参考资料:

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

4

2 回答 2

10

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

我的调查显示,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。

于 2013-11-29T14:15:18.627 回答
4

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

于 2013-11-07T00:20:05.013 回答