红米note 4x 视频播放切后台黑屏

2,297 阅读3分钟

问题

使用mediaPlay+TextureView播放和显示视频,在红米note4x上视频播放时切后台返回时视频区域黑屏,提示音频可以继续播放,在其他手机上播放时没有这种问题。

代码

class VideoView(context: Context, attrs: AttributeSet? = null) : TextureView(context, attrs), TextureView.SurfaceTextureListener, LifecycleObserver {

    private var mediaPlayer: MediaPlayer? = null
    private var surface: Surface? = null
    private var resId: Int = 0

    init {
        if (context is FragmentActivity) {
            context.lifecycle.addObserver(this)
        }
        surfaceTextureListener = this
    }

    fun setVideoId(resId: Int) {
        this.resId = resId
        mediaPlayer = MediaPlayer.create(context, resId)
    }

    fun start() {
        mediaPlayer?.start()
    }

    fun release() {
        mediaPlayer?.apply {
            this.stop()
            this.release()
        }
        surfaceTexture?.release()
        mediaPlayer = null
    }

    private fun initSurface() {
        surface = Surface(surfaceTexture)
        mediaPlayer?.setSurface(surface)
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    fun destroy() {
        release()
        if (context is FragmentActivity) {
            (context as FragmentActivity).lifecycle.removeObserver(this)
        }
    }

    override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
        initSurface()
    }

    override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}
    override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
		release()
        return false
    }

    override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}
}

问题分析

在红米note 4x手机上切后台时会调用onSurfaceTextureDestroyed,由于在onSurfaceTextureDestroyed中会调用release()在release()中会进行播放器和资源的release,所以返回前台时调用onSurfaceTextureAvailable后由于播放器是空,所以展示黑屏。 1、红米note 4x调用栈 红米note切后台 如下图 在这里插入图片描述 调用栈断点打印出来的堆栈信息

onSurfaceTextureDestroyed:91, MyVideoView (com.bytedance.minddance.android.devicecheck.view)
destroySurface:231, TextureView (android.view)
destroyHardwareResources:364, TextureView (android.view)
destroyResources:518, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyResources:525, ThreadedRenderer (android.view)
destroyHardwareResources:513, ThreadedRenderer (android.view)
setWindowStopped:1186, ViewRootImpl (android.view)
setStoppedState:639, WindowManagerGlobal (android.view)
performStop:7023, Activity (android.app)
performStopActivityInner:3965, ActivityThread (android.app)
handleStopActivity:4023, ActivityThread (android.app)
-wrap25:-1, ActivityThread (android.app)
handleMessage:1577, ActivityThread$H (android.app)
dispatchMessage:102, Handler (android.os)
loop:165, Looper (android.os)
main:6375, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:883, ZygoteInit$MethodAndArgsCaller (com.android.internal.os)
main:773, ZygoteInit (com.android.internal.os)

根据红米Android系统版本查找源码android-7.0.0_r6:TextureView.java

private void destroySurface() {
        if (mLayer != null) {
            mLayer.detachSurfaceTexture();

            boolean shouldRelease = true;
            if (mListener != null) {
                shouldRelease = mListener.onSurfaceTextureDestroyed(mSurface);
            }

            synchronized (mNativeWindowLock) {
                nDestroyNativeWindow();
            }

            mLayer.destroy();
            if (shouldRelease) mSurface.release();
            mSurface = null;
            mLayer = null;

            // Make sure if/when new layer gets re-created, transform matrix will
            // be re-applied.
            mMatrixChanged = true;
            mHadSurface = true;
        }
    }

mListener.onSurfaceTextureDestroyed(mSurface)方法是在destroySurface() 中调用的。 在note 4x中切后台调用stopActivity后会调用destorySurface方法,导致mListener.onSurfaceTextureDestroyed(mSurface)调用并且释放了mediaPlay相关资源,切回前台后才会展示黑屏。 注意:同时查看了其他版本的TextureView没有找到destroySurface方法。也就是说只有在这个版本上才会有这个问题。

2、其他手机调用栈 华为手机只有在退出当前activity时才会调用onSurfaceTextureDestroyed方法, 在这里插入图片描述

下面是退出时的调用栈

onSurfaceTextureDestroyed:91, MyVideoView (com.bytedance.minddance.android.devicecheck.view)
releaseSurfaceTexture:261, TextureView (android.view)
onDetachedFromWindowInternal:232, TextureView (android.view)
dispatchDetachedFromWindow:19819, View (android.view)
dispatchDetachedFromWindow:4010, ViewGroup (android.view)
dispatchDetachedFromWindow:4010, ViewGroup (android.view)
dispatchDetachedFromWindow:4010, ViewGroup (android.view)
dispatchDetachedFromWindow:4010, ViewGroup (android.view)
removeViewInternal:5653, ViewGroup (android.view)
removeViewInternal:5624, ViewGroup (android.view)
removeView:5555, ViewGroup (android.view)
moveToState:1248, FragmentManager (androidx.fragment.app)
moveToState:1354, FragmentManager (androidx.fragment.app)
moveFragmentToExpectedState:1432, FragmentManager (androidx.fragment.app)
moveToState:1495, FragmentManager (androidx.fragment.app)
dispatchStateChange:2617, FragmentManager (androidx.fragment.app)
dispatchDestroy:2601, FragmentManager (androidx.fragment.app)
dispatchDestroy:330, FragmentController (androidx.fragment.app)
onDestroy:365, FragmentActivity (androidx.fragment.app)
onDestroy:242, AppCompatActivity (androidx.appcompat.app)
onDestroy:435, BaseActivity (com.bytedance.minddance.android.ui.base)
onDestroy:309, BaseSlideBackActivity (com.bytedance.minddance.android.ui.base)
performDestroy:8349, Activity (android.app)
callActivityOnDestroy:1355, Instrumentation (android.app)
performDestroyActivity:5670, ActivityThread (android.app)
handleDestroyActivity:5715, ActivityThread (android.app)
execute:44, DestroyActivityItem (android.app.servertransaction)
executeLifecycleState:190, TransactionExecutor (android.app.servertransaction)
execute:105, TransactionExecutor (android.app.servertransaction)
handleMessage:2473, ActivityThread$H (android.app)
dispatchMessage:110, Handler (android.os)
loop:219, Looper (android.os)
main:8349, ActivityThread (android.app)
invoke:-1, Method (java.lang.reflect)
run:513, RuntimeInit$MethodAndArgsCaller (com.android.internal.os)
main:1055, ZygoteInit (com.android.internal.os)

华为手机上在切后台时不会调用到onSurfaceTextureDestroyed方法。

4、解决方法

上面的问题是由于切后台时调用onSurfaceTextureDestroyed方法,并且在onSurfaceTextureDestroyed中stopPlayback中对mediaPlay进行了释放,所以切回前台时导致黑屏,解决方法是去掉onSurfaceTextureDestroyed中的release()方法. 总结 Android系统版本android-7.0.0_r6这个版本的切后台都会有这种问题。

参考

1、stackoverflow.com/questions/6…

2、stackoverflow.com/questions/3…

3、developer.android.com/reference/a…

4、developer.android.com/guide/topic…

5、developer.android.com/reference/a…

6、blog.mindorks.com/using-media…