"Поверхность была выпущена" внутри "surfaceCreated"

Я знаю, что это общий вопрос, однако эта трассировка стека показывает, что что-то еще не так. Вы можете видеть, что хотя setDisplay(holder) вызывается внутри surfaceCreated, он все равно бросает IllegalArgumentException. Это тоже не редкое исключение, вчера произошедшее ~ 125 000 раз в ~ 3 000 000 просмотров клипов. Я могу заверить вас, что mCurrentPlayer также инициализируется правильно.

surfaceCreated:

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

surfaceDestroy:

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

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

StackTrace:

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 можно найти здесь:

Здесь я испытываю недостаток в знаниях на С++, похоже, что он пытается зафиксировать на поверхности, и если он не может вернуть поверхность, будет null. Когда он будет возвращен как null, будет сброшен IllegalArgumentException.

Ответ 1

Я только что столкнулся с подобной проблемой.

И мое исследование показывает, что в SurfaceView обнаружена ошибка, которая вызывает передачу недопустимой поверхности методу обратного вызова surfaceCreated.

Это фиксация в репозитории android, которая ее исправляет: ссылка.

Кажется, что исправление в источниках android было введено в версии 4.2. И, из-за сбоев приложения, я вижу, что авария, вызванная недопустимой поверхностью, происходила только на 4.0 и 4.1.

Итак, я могу предположить, что до 4.0 было правдой передать недопустимую поверхность MediaPlayer. И было изменение в логике SurfaceView/MediaPlayer в 4.0, из-за чего это уже недействительно. Но код в SurfaceView не обновлялся до 4.2 (в котором эта проблема в SurfaceView исправлена).

Я проверил в git repo андроида, и действительно, версия с тегами android-4.0.1_r1 не включает исправления и теги с тегами, в которые входит android-4.2.1_r1.

Итак, чтобы исправить это для платформ, которые не содержат исправления для него, вручную проверьте, действительна ли поверхность до ее установки в MediaPlayer, только для платформ 4.0 и после:

@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 и более поздних поверхностях с обработанной поверхностью просто не будет вызываться с недопустимой поверхностью.

Ответ 2

У меня были такие проблемы, как в прошлом, с помощью Android VideoViews/MediaPlayers. Оказалось, что основной SurfaceView собирал мусор. Я решил это, добавив onPreparedLister в MediaPlayer, а затем явную ссылку на него в своем классе, пока я его использовал. Возможно, это помогает.