GLSurfaceView黑屏问题解决

本文深入探讨了GLSurfaceView在切换应用前后台时出现短暂黑屏的问题,分析了其背后的工作机制,并提供了解决方案。通过重写onWindowVisibilityChanged方法,避免了资源回收导致的黑屏现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题列表

  1. 打开其他页面返回当前页面 GLSurfaceView会有短暂黑屏
  2. 按HOME键回到后台再切换回来 GLSurfaceView会有短暂黑屏

分析

以上问题 总结下就是回到后台后再切换到前台, GLSurfaceView会有短暂黑屏

提出问题

1.GLSurfaceView 回到后台做了什么

2.GLSurfaceView 回到前台做了什么

GLSurfaceView 回到后台以及前台,那么关注onWindowVisibilityChanged 就可以了,我们看下相关源码

@Override
 protected void onWindowVisibilityChanged(int visibility) {
     super.onWindowVisibilityChanged(visibility);
     mWindowVisibility = visibility == VISIBLE;
     mRequestedVisible = mWindowVisibility && mViewVisibility;
     updateWindow(false, false);
 }
 
 protected void updateWindow(boolean force, boolean redrawNeeded) {
     if (!mHaveFrame) {
         return;
     }
     ViewRootImpl viewRoot = getViewRootImpl();
     if (viewRoot != null) {
         mTranslator = viewRoot.mTranslator;
     }

     if (mTranslator != null) {
         mSurface.setCompatibilityTranslator(mTranslator);
     }

     int myWidth = mRequestedWidth;
     if (myWidth <= 0) myWidth = getWidth();
     int myHeight = mRequestedHeight;
     if (myHeight <= 0) myHeight = getHeight();

     getLocationInWindow(mLocation);
     final boolean creating = mWindow == null;
     final boolean formatChanged = mFormat != mRequestedFormat;
     final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
     final boolean visibleChanged = mVisible != mRequestedVisible;

     if (force || creating || formatChanged || sizeChanged || visibleChanged
         || mLeft != mLocation[0] || mTop != mLocation[1]
         || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) {

         if (DEBUG) Log.i(TAG, "Changes: creating=" + creating
                 + " format=" + formatChanged + " size=" + sizeChanged
                 + " visible=" + visibleChanged
                 + " left=" + (mLeft != mLocation[0])
                 + " top=" + (mTop != mLocation[1]));

         try {
             final boolean visible = mVisible = mRequestedVisible;
             mLeft = mLocation[0];
             mTop = mLocation[1];
             mWidth = myWidth;
             mHeight = myHeight;
             mFormat = mRequestedFormat;

             // Scaling/Translate window's layout here because mLayout is not used elsewhere.

             // Places the window relative
             mLayout.x = mLeft;
             mLayout.y = mTop;
             mLayout.width = getWidth();
             mLayout.height = getHeight();
             if (mTranslator != null) {
                 mTranslator.translateLayoutParamsInAppWindowToScreen(mLayout);
             }

             mLayout.format = mRequestedFormat;
             mLayout.flags |=WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                           | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                           | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
                           | WindowManager.LayoutParams.FLAG_SCALED
                           | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                           | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                           ;
             if (!getContext().getResources().getCompatibilityInfo().supportsScreen()) {
                 mLayout.privateFlags |=
                         WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
             }
             mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;

             if (mWindow == null) {
                 Display display = getDisplay();
                 mWindow = new MyWindow(this);
                 mLayout.type = mWindowType;
                 mLayout.gravity = Gravity.START|Gravity.TOP;
                 mSession.addToDisplayWithoutInputChannel(mWindow, mWindow.mSeq, mLayout,
                         mVisible ? VISIBLE : GONE, display.getDisplayId(), mContentInsets,
                         mStableInsets);
             }

             boolean realSizeChanged;
             boolean reportDrawNeeded;

             int relayoutResult;

             mSurfaceLock.lock();
             try {
                 mUpdateWindowNeeded = false;
                 reportDrawNeeded = mReportDrawNeeded;
                 mReportDrawNeeded = false;
                 mDrawingStopped = !visible;

                 if (DEBUG) Log.i(TAG, "Cur surface: " + mSurface);

                 relayoutResult = mSession.relayout(
                     mWindow, mWindow.mSeq, mLayout, mWidth, mHeight,
                         visible ? VISIBLE : GONE,
                         WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY,
                         mWinFrame, mOverscanInsets, mContentInsets,
                         mVisibleInsets, mStableInsets, mOutsets, mConfiguration,
                         mNewSurface);
                 if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
                     reportDrawNeeded = true;
                 }

                 if (DEBUG) Log.i(TAG, "New surface: " + mNewSurface
                         + ", vis=" + visible + ", frame=" + mWinFrame);

                 mSurfaceFrame.left = 0;
                 mSurfaceFrame.top = 0;
                 if (mTranslator == null) {
                     mSurfaceFrame.right = mWinFrame.width();
                     mSurfaceFrame.bottom = mWinFrame.height();
                 } else {
                     float appInvertedScale = mTranslator.applicationInvertedScale;
                     mSurfaceFrame.right = (int) (mWinFrame.width() * appInvertedScale + 0.5f);
                     mSurfaceFrame.bottom = (int) (mWinFrame.height() * appInvertedScale + 0.5f);
                 }

                 final int surfaceWidth = mSurfaceFrame.right;
                 final int surfaceHeight = mSurfaceFrame.bottom;
                 realSizeChanged = mLastSurfaceWidth != surfaceWidth
                         || mLastSurfaceHeight != surfaceHeight;
                 mLastSurfaceWidth = surfaceWidth;
                 mLastSurfaceHeight = surfaceHeight;
             } finally {
                 mSurfaceLock.unlock();
             }

             try {
                 redrawNeeded |= creating | reportDrawNeeded;

                 SurfaceHolder.Callback callbacks[] = null;

                 final boolean surfaceChanged = (relayoutResult
                         & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0;
                 // 重点关注点1 在window 不显示时,调用了SurfaceHolder的surfaceDestroyed方法 并把mSurfaceCreated=false
                 if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) {
                     mSurfaceCreated = false;
                     if (mSurface.isValid()) {
                         if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed");
                         callbacks = getSurfaceCallbacks();
                         for (SurfaceHolder.Callback c : callbacks) {
                             c.surfaceDestroyed(mSurfaceHolder);
                         }
                     }
                 }

                 mSurface.transferFrom(mNewSurface);

                 if (visible && mSurface.isValid()) {
                 // 重点关注点2 在window 显示时,会触发这里,先surfaceCreated 后surfaceChanged
                     if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) {
                         mSurfaceCreated = true;
                         mIsCreating = true;
                         if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated");
                         if (callbacks == null) {
                             callbacks = getSurfaceCallbacks();
                         }
                         for (SurfaceHolder.Callback c : callbacks) {
                             c.surfaceCreated(mSurfaceHolder);
                         }
                     }
                     
                     if (creating || formatChanged || sizeChanged
                             || visibleChanged || realSizeChanged) {
                         if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat
                                 + " w=" + myWidth + " h=" + myHeight);
                         if (callbacks == null) {
                             callbacks = getSurfaceCallbacks();
                         }
                         for (SurfaceHolder.Callback c : callbacks) {
                             c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight);
                         }
                     }
                     if (redrawNeeded) {
                         if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded");
                         if (callbacks == null) {
                             callbacks = getSurfaceCallbacks();
                         }
                         for (SurfaceHolder.Callback c : callbacks) {
                             if (c instanceof SurfaceHolder.Callback2) {
                                 ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded(
                                         mSurfaceHolder);
                             }
                         }
                     }
                 }
             } finally {
                 mIsCreating = false;
                 if (redrawNeeded) {
                     if (DEBUG) Log.i(TAG, "finishedDrawing");
                     mSession.finishDrawing(mWindow);
                 }
                 mSession.performDeferredDestroy(mWindow);
             }
         } catch (RemoteException ex) {
         }
         if (DEBUG) Log.v(
             TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
             " w=" + mLayout.width + " h=" + mLayout.height +
             ", frame=" + mSurfaceFrame);
     }
 }
 

上边列出了两个重点关注点 ,1是回到后台的逻辑,2是回到前台的逻辑

那么问题的原因就算找到了,找到了的话如何解决。

解决

首先这里回到后台回收是为了回收部分资源,让性能更好利用起来,但是出现了一些体验问题

不建议我们总是违背系统意愿做自己想做的事

解决方案就是不触发回收,如何不触发回收,重写onWindowVisibilityChanged,如下

```
override fun onWindowVisibilityChanged(visibility: Int) {
    super.onWindowVisibilityChanged(View.VISIBLE)
    // 以下是优化建议,在显示不显示时处理一下后台资源
    if (visibility == View.VISIBLE) {
        ResumeThread()
    } else {
        SuspendThread()
    }
}
```
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值