Android6.0 WMS(七) 窗口Z轴位置

转载: https://blog.csdn.net/kc58236582/article/details/53893269


通过前面几篇文章的学习,我们知道了在Android系统中,无论是普通的Activity窗口,还是特殊的输入法窗口和壁纸窗口,它们都是被WindowManagerService服务组织在一个窗口堆栈中的,其中,Z轴位置较大的窗口排列在Z轴位置较小的窗口的上面。有了这个窗口堆栈之后,WindowManagerService服务就可以按照一定的规则计算每一个窗口的Z轴位置了,这个在之前的http://blog.csdn.net/kc58236582/article/details/53519710#t0的博客中分析过这里我们会再提到,而再把这个Z轴设置到SurfaceFlinger中我们会在这篇博客中分析。

基于窗口堆栈来计算窗口的Z轴位置是比较有意思的。按照一般的理解,应该是先计算好窗口的Z轴位置,然后再按照Z轴位置的大小来将各个窗口排列在堆栈中。但是,事实上,窗口是按照其它规则排列在堆栈中。这些规则与窗口的类型、创建顺序和运行状态等有关。例如,状态栏窗口总是位于堆栈的顶端,输入法窗口总是位于需要输入法的窗口的上面,而壁纸窗口总是位于需要显示壁纸的窗口的下面。又如,当一个Activity组件从后台激活到前台时,与它所对应的窗口就会被相应地移动到窗口堆栈的上面去。

窗口的UI最终是需要通过SurfaceFlinger服务来统一渲染的,而SurfaceFlinger服务在渲染窗口的UI之前,需要计算基于各个窗口的Z轴位置来计算它们的可见区域。因此,WindowManagerService服务计算好每一个窗口的Z轴位置之后,还需要将它们设置到SurfaceFlinger服务中去,以便SurfaceFlinger服务可以正确地渲染每一个窗口的UI。

上述窗口的Z轴位置计算和设置过程如图1所示:

图1 窗口Z轴位置的计算和设置过程

接下来,我们就首先分析两个需要重新计算窗口Z轴位置的情景,接着再分析窗口的Z轴位置的计算过程,最后分析WindowManagerService服务将窗口的Z轴设置到SurfaceFlinger服务中去的过程。


一、重新计算Z轴的情景

这里主要分析两个需要重新计算窗口Z轴位置的情景:应用程序增加一个窗口到WindowManagerService服务和应用程序请求WindowManagerService服务重新布局一个窗口。下面我们分两个小结分别介绍下这两个地方。

1.1 addWindow

应用程序请求增加一个窗口到WindowManagerService服务的时候,最终会调用到WindowManagerService类的成员函数addWindow。接下来我们就主要分析这个函数与重新计算窗口Z轴位置相关的逻辑,如下所示:


  
  
  1. public int addWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
  3. Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
  4. InputChannel outInputChannel) {
  5. ......
  6. boolean addToken = false;
  7. WindowToken token = mTokenMap.get(attrs.token);
  8. ......
  9. WindowState win = new WindowState( this, session, client, token,
  10. attachedWindow, appOp[ 0], seq, attrs, viewVisibility, displayContent);
  11. ......
  12. if (addToken) {
  13. mTokenMap.put(attrs.token, token);
  14. }
  15. win.attach();
  16. mWindowMap.put(client.asBinder(), win);
  17. ......
  18. boolean imMayMove = true;
  19. if (type == TYPE_INPUT_METHOD) {
  20. win.mGivenInsetsPending = true;
  21. mInputMethodWindow = win;
  22. addInputMethodWindowToListLocked(win);
  23. imMayMove = false;
  24. } else if (type == TYPE_INPUT_METHOD_DIALOG) {
  25. mInputMethodDialogs.add(win);
  26. addWindowToListInOrderLocked(win, true);
  27. moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked( true));
  28. imMayMove = false;
  29. } else {
  30. addWindowToListInOrderLocked(win, true);
  31. if (type == TYPE_WALLPAPER) {
  32. mLastWallpaperTimeoutTime = 0;
  33. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  34. } else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
  35. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  36. } else if (mWallpaperTarget != null
  37. && mWallpaperTarget.mLayer >= win.mBaseLayer) {
  38. // If there is currently a wallpaper being shown, and
  39. // the base layer of the new window is below the current
  40. // layer of the target window, then adjust the wallpaper.
  41. // This is to avoid a new window being placed between the
  42. // wallpaper and its target.
  43. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
  44. }
  45. }
  46. ......
  47. mInputMonitor.setUpdateInputWindowsNeededLw();
  48. boolean focusChanged = false;
  49. if (win.canReceiveKeys()) {
  50. focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
  51. false /*updateInputWindows*/);
  52. if (focusChanged) {
  53. imMayMove = false;
  54. }
  55. }
  56. if (imMayMove) {
  57. moveInputMethodWindowsIfNeededLocked( false);
  58. }
  59. assignLayersLocked(displayContent.getWindowList());
  60. ......
  61. }
  62. ......
  63. }

之前的博客我们分析过这个函数这里再针对窗口位置再总结下:

WindowManagerService类的成员函数addWindow会根据当前正在添加的窗口的类型来调用不同的成员函数来向窗口堆栈的合适位置插入一个WindowState对象,即:

        1. 如果添加的是一个输入法窗口,那么就调用成员函数addInputMethodWindowToListLocked将它放置在需要显示输入法的窗口的上面去;

        2. 如果添加的是一个输入法对话框,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustInputMethodDialogsLocked来将它放置在输入法窗口的上面;

        3. 如果添加的是一个普通窗口,那么就直接调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中;

        4. 如果添加的是一个普通窗口,并且这个窗口需要显示壁纸,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustWallpaperWindowsLocked来将壁纸窗口放置在它的下面。

        5. 如果添加的是一个壁纸窗口,那么就先调用成员函数addWindowToListInOrderLocked来将它插入到窗口堆栈中,接着再调用成员函数adjustWallpaperWindowsLocked来将它放置在需要显示壁纸的窗口的下面。

        无论如何,WindowManagerService类的成员函数addWindow最终都会调用成员函数assignLayersLocked来重新计算系统中所有窗口的Z轴位置,这是因为前面往窗口堆栈增加了一个新的窗口。


1.2 relayoutWindow

应用程序进程请求WindowManagerService服务重新布局一个窗口的时候,最终会调用到WindowManagerService类的成员函数relayoutWindow。接下来我们就主要分析这个函数与重新计算窗口Z轴位置相关的逻辑,如下所示:


  
  
  1. public int relayoutWindow(Session session, IWindow client, int seq,
  2. WindowManager.LayoutParams attrs, int requestedWidth,
  3. int requestedHeight, int viewVisibility, int flags,
  4. Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
  5. Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Configuration outConfig,
  6. Surface outSurface) {
  7. ......
  8. synchronized(mWindowMap) {
  9. WindowState win = windowForClientLocked(session, client, false);
  10. if (win == null) {
  11. return 0;
  12. }
  13. WindowStateAnimator winAnimator = win.mWinAnimator;
  14. ......
  15. boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0;
  16. final boolean isDefaultDisplay = win.isDefaultDisplay();
  17. boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
  18. || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
  19. || (!win.mRelayoutCalled));
  20. boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
  21. && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
  22. wallpaperMayMove |= (flagChanges & FLAG_SHOW_WALLPAPER) != 0;
  23. ......
  24. if (focusMayChange) {
  25. if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
  26. false /*updateInputWindows*/)) {
  27. imMayMove = false;
  28. }
  29. }
  30. // updateFocusedWindowLocked() already assigned layers so we only need to
  31. // reassign them at this point if the IM window state gets shuffled
  32. if (imMayMove && (moveInputMethodWindowsIfNeededLocked( false) || toBeDisplayed)) {
  33. // Little hack here -- we -should- be able to rely on the
  34. // function to return true if the IME has moved and needs
  35. // its layer recomputed. However, if the IME was hidden
  36. // and isn't actually moved in the list, its layer may be
  37. // out of data so we make sure to recompute it.
  38. assignLayersLocked(win.getWindowList());
  39. }
  40. ......
  41. return (inTouchMode ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0)
  42. | (toBeDisplayed ? WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME : 0)
  43. | (surfaceChanged ? WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED : 0);
  44. }

        1. 如果系统当前获得焦点的窗口可能发生了变化,那么就会调用成员函数updateFocusedWindowLocked来重新计算系统当前应该获得焦点的窗口。如果系统当前获得焦点的窗口真的发生了变化,即窗口堆栈的窗口排列发生了变化,那么在调用成员函数updateFocusedWindowLocked的时候,就会调用成员函数assignLayersLocked来重新计算系统中所有窗口的Z轴位置。

        2. 如果系统中的输入法窗口可能需要移动,那么就会调用成员函数moveInputMethodWindowsIfNeededLocked来检查是否真的需要移动输入法窗口。如果需要移动,那么成员函数moveInputMethodWindowsIfNeededLocked的返回值就会等于true,这时候就说明输入法窗口在窗口堆栈中的位置发生了变化,因此,就会调用assignLayersLocked函数来重新计算系统中所有窗口的Z轴位置。

        3. 如果当前正在请求调整其布局的窗口是由不可见变化可见的,即变量toBeDisplayed的值等于true,那么接下来也是需要重新计算系统中所有窗口的Z轴位置的。


二、计算Z轴位置

从前面第一部分的内容可以知道,一旦窗口堆栈中的窗口发生了变化,那么WindowManagerService类的成员函数assignLayersLocked就会调用来计算系统中所有窗口的Z轴位置。

        窗口的Z轴位置除了与它在窗口堆栈中的位置有关之外,还与窗口的类型有关。窗口的类型在创建的时候就已经是确定了的,WindowManagerService服务在为它创建一个WindowState对象的时候,就会根据它的类型得到一个BaseLayer值,这个BaseLayer值在计算它的Z轴位置的时候会用到。还有就是assignLayersLocked函数的分析。

这两部分都在Android6.0 WMS(三) WMS窗口次序 这篇博客中分析过了,这里我们就不分析了。


三、设置窗口的Z轴位置到SurfaceFlinger服务中去

 WindowManagerService服务在刷新系统的UI的时候,就会将系统中已经计算好了的窗口Z轴位置设置到SurfaceFlinger服务中去,以便SurfaceFlinger服务可以对系统中的窗口进行可见性计算以及合成和渲染等操作。

刷新系统UI是通过调用WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLockedInner来实现的,接下来我们就分析这个成员函数与设置窗口的Z轴位置到SurfaceFlinger服务中去相关的逻辑。

        为了方便描述设置窗口的Z轴位置到SurfaceFlinger服务中去的过程,我们先列出WindowManagerService类的成员函数performLayoutAndPlaceSurfacesLockedInner的实现架构,如下所示:


  
  
  1. private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
  2. ......
  3. SurfaceControl.openTransaction();
  4. try {
  5. ......
  6. do {
  7. repeats++;
  8. if (repeats > 6) {
  9. Slog.w(TAG, "Animation repeat aborted after too many iterations");
  10. displayContent.layoutNeeded = false;
  11. break;
  12. }
  13. ......
  14. // FIRST LOOP: Perform a layout, if needed.第一个循环
  15. if (repeats < 4) {
  16. performLayoutLockedInner(displayContent, repeats == 1, //计算Activity窗口大小
  17. false /*updateInputWindows*/);
  18. } else {
  19. Slog.w(TAG, "Layout repeat skipped after too many iterations");
  20. }
  21. // FIRST AND ONE HALF LOOP: Make WindowManagerPolicy think
  22. // it is animating.
  23. displayContent.pendingLayoutChanges = 0;
  24. if (isDefaultDisplay) {
  25. mPolicy.beginPostLayoutPolicyLw(dw, dh);
  26. for (i = windows.size() - 1; i >= 0; i--) { //有一个循环
  27. WindowState w = windows.get(i);
  28. if (w.mHasSurface) {
  29. mPolicy.applyPostLayoutPolicyLw(w, w.mAttrs, w.mAttachedWindow);
  30. }
  31. }
  32. displayContent.pendingLayoutChanges |= mPolicy.finishPostLayoutPolicyLw();
  33. if (DEBUG_LAYOUT_REPEATS) debugLayoutRepeats(
  34. "after finishPostLayoutPolicyLw", displayContent.pendingLayoutChanges);
  35. }
  36. } while (displayContent.pendingLayoutChanges != 0);
  37. mInnerFields.mObscured = false;
  38. mInnerFields.mSyswin = false;
  39. displayContent.resetDimming();
  40. // Only used if default window
  41. final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
  42. final int N = windows.size(); //循环遍历所有的窗口
  43. for (i=N -1; i>= 0; i--) {
  44. WindowState w = windows.get(i);
  45. final TaskStack stack = w.getStack();
  46. if ( stack == null && w.getAttrs().type != TYPE_PRIVATE_PRESENTATION) {
  47. continue;
  48. }
  49. final boolean obscuredChanged = w.mObscured != mInnerFields.mObscured;
  50. // Update effect.
  51. w.mObscured = mInnerFields.mObscured;
  52. if (!mInnerFields.mObscured) {
  53. handleNotObscuredLocked(w, innerDw, innerDh);
  54. }
  55. if ( stack != null && ! stack.testDimmingTag()) {
  56. handleFlagDimBehind(w);
  57. }
  58. if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
  59. && w.isVisibleLw()) {
  60. // This is the wallpaper target and its obscured state
  61. // changed... make sure the current wallaper's visibility
  62. // has been updated accordingly.
  63. updateWallpaperVisibilityLocked();
  64. }
  65. final WindowStateAnimator winAnimator = w.mWinAnimator;
  66. ......
  67. if (w.mHasSurface) { // 当其有Surface代表有layer数据
  68. // Take care of the window being ready to display.
  69. ......
  70. winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
  71. }
  72. ......
  73. mDisplayManagerInternal.setDisplayProperties(displayId,
  74. mInnerFields.mDisplayHasContent, mInnerFields.mPreferredRefreshRate,
  75. mInnerFields.mPreferredModeId,
  76. true /* inTraversal, must call performTraversalInTrans... below */);
  77. getDisplayContentLocked(displayId).stopDimmingIfNeeded();
  78. if (updateAllDrawn) {
  79. updateAllDrawnLocked(displayContent);
  80. }
  81. }
  82. if (focusDisplayed) {
  83. mH.sendEmptyMessage(H.REPORT_LOSING_FOCUS);
  84. }
  85. // Give the display manager a chance to adjust properties
  86. // like display rotation if it needs to.
  87. mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
  88. } catch (RuntimeException e) {
  89. Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
  90. } finally {
  91. SurfaceControl.closeTransaction();
  92. }
  93. ......
  94. }

上面这个函数之前在分析计算Activity窗口大小的时候我们分析过了,这个函数以SurfaceControl.openTransaction开始,以SurfaceControl.closeTransaction结束。之前是分析的performLayoutLockedInner函数(计算Activity窗口大小),这里我们遍历所有的窗口,调用每个WindowState的winAnimator的setSurfaceBoundariesLocked函数来设置窗口大小,位置等到SurfaceFlinger中。

我们来看下WindowStateAnimator的setSurfaceBoundariesLocked函数,这里只是设置宽度,高度,位置和变化矩阵到SurfaceFlinger中。


  
  
  1. void setSurfaceBoundariesLocked(final boolean recoveringMemory) {
  2. final WindowState w = mWin;
  3. int width;
  4. int height;
  5. if ((w.mAttrs.flags & LayoutParams.FLAG_SCALED) != 0) {
  6. width = w.mRequestedWidth;
  7. height = w.mRequestedHeight;
  8. } else {
  9. width = w.mCompatFrame.width();
  10. height = w.mCompatFrame.height();
  11. }
  12. // Something is wrong and SurfaceFlinger will not like this,
  13. // try to revert to sane values
  14. if (width < 1) {
  15. width = 1;
  16. }
  17. if (height < 1) {
  18. height = 1;
  19. }
  20. float left = w.mShownFrame.left;
  21. float top = w.mShownFrame.top;
  22. ......
  23. width += scale * (attrs.surfaceInsets.left + attrs.surfaceInsets.right);
  24. height += scale * (attrs.surfaceInsets.top + attrs.surfaceInsets.bottom);
  25. left -= scale * attrs.surfaceInsets.left;
  26. top -= scale * attrs.surfaceInsets.top;
  27. final boolean surfaceMoved = mSurfaceX != left || mSurfaceY != top;
  28. if (surfaceMoved) {
  29. mSurfaceX = left;
  30. mSurfaceY = top;
  31. try {
  32. if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
  33. "POS " + left + ", " + top, null);
  34. mSurfaceControl.setPosition(left, top); //设置位置
  35. } catch (RuntimeException e) {
  36. Slog.w(TAG, "Error positioning surface of " + w
  37. + " pos=(" + left + "," + top + ")", e);
  38. if (!recoveringMemory) {
  39. mService.reclaimSomeSurfaceMemoryLocked( this, "position", true);
  40. }
  41. }
  42. }
  43. final boolean surfaceResized = mSurfaceW != width || mSurfaceH != height;
  44. if (surfaceResized) {
  45. mSurfaceW = width;
  46. mSurfaceH = height;
  47. mSurfaceResized = true;
  48. try {
  49. if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
  50. "SIZE " + width + "x" + height, null);
  51. mSurfaceControl.setSize(width, height); //设置大小
  52. mSurfaceControl.setMatrix( //变换矩阵
  53. mDsDx * w.mHScale, mDtDx * w.mVScale,
  54. mDsDy * w.mHScale, mDtDy * w.mVScale);
  55. mAnimator.setPendingLayoutChanges(w.getDisplayId(),
  56. WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);
  57. if ((w.mAttrs.flags & LayoutParams.FLAG_DIM_BEHIND) != 0) {
  58. final TaskStack stack = w.getStack();
  59. if ( stack != null) {
  60. stack.startDimmingIfNeeded( this);
  61. }
  62. }
  63. } catch (RuntimeException e) {
  64. if (!recoveringMemory) {
  65. mService.reclaimSomeSurfaceMemoryLocked( this, "size", true);
  66. }
  67. }
  68. }
  69. updateSurfaceWindowCrop(recoveringMemory);
  70. }

而设置窗口的Z轴位置,只有在创建SurfaceControl的时候和动画开始的时候会设置Z轴位置,下面我们先看创建SurfaceControl的时候。


  
  
  1. SurfaceControl createSurfaceLocked() {
  2. final WindowState w = mWin;
  3. if (mSurfaceControl == null) {
  4. ......
  5. mSurfaceFormat = format;
  6. if (DEBUG_SURFACE_TRACE) {
  7. mSurfaceControl = new SurfaceTrace(
  8. mSession.mSurfaceSession,
  9. attrs.getTitle().toString(),
  10. width, height, format, flags);
  11. } else {
  12. mSurfaceControl = new SurfaceControl( //创建SurfaceControl对象
  13. mSession.mSurfaceSession,
  14. attrs.getTitle().toString(),
  15. width, height, format, flags);
  16. }
  17. ......
  18. // Start a new transaction and apply position & offset.
  19. SurfaceControl.openTransaction();
  20. try {
  21. mSurfaceX = left;
  22. mSurfaceY = top;
  23. try {
  24. mSurfaceControl.setPosition(left, top);
  25. mSurfaceLayer = mAnimLayer;
  26. final DisplayContent displayContent = w.getDisplayContent();
  27. if (displayContent != null) {
  28. mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
  29. }
  30. mSurfaceControl.setLayer(mAnimLayer); //设置Z轴位置
  31. mSurfaceControl.setAlpha( 0);
  32. mSurfaceShown = false;
  33. } catch (RuntimeException e) {
  34. mService.reclaimSomeSurfaceMemoryLocked( this, "create-init", true);
  35. }
  36. mLastHidden = true;
  37. } finally {
  38. SurfaceControl.closeTransaction();
  39. }
  40. }
  41. return mSurfaceControl;
  42. }

还有动画开始的时候也会设置Z轴位置,这个在Android6.0 WMS(六) WMS动画管理 中分析过。


   
   
  1. public void prepareSurfaceLocked(final boolean recoveringMemory) {
  2. final WindowState w = mWin;
  3. ……
  4. if (mIsWallpaper && !mWin.mWallpaperVisible) {
  5. // Wallpaper is no longer visible and there is no wp target => hide it.
  6. hide();
  7. } else if (w.mAttachedHidden || !w.isOnScreen()) {
  8. hide();
  9. ……
  10. } else if (mLastLayer != mAnimLayer
  11. || mLastAlpha != mShownAlpha
  12. || mLastDsDx != mDsDx
  13. || mLastDtDx != mDtDx
  14. || mLastDsDy != mDsDy
  15. || mLastDtDy != mDtDy
  16. || w.mLastHScale != w.mHScale
  17. || w.mLastVScale != w.mVScale
  18. || mLastHidden) {
  19. displayed = true;
  20. mLastAlpha = mShownAlpha;
  21. mLastLayer = mAnimLayer;
  22. mLastDsDx = mDsDx;
  23. mLastDtDx = mDtDx;
  24. mLastDsDy = mDsDy;
  25. mLastDtDy = mDtDy;
  26. w.mLastHScale = w.mHScale;
  27. w.mLastVScale = w.mVScale;
  28. if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
  29. “alpha=” + mShownAlpha + ” layer=” + mAnimLayer
  30. + ” matrix=[“ + mDsDx + “*” + w.mHScale
  31. + “,” + mDtDx + “*” + w.mVScale
  32. + “][“ + mDsDy + “*” + w.mHScale
  33. + “,” + mDtDy + “*” + w.mVScale + “]”, null);
  34. if (mSurfaceControl != null) {
  35. try {
  36. mSurfaceAlpha = mShownAlpha;
  37. mSurfaceControl.setAlpha(mShownAlpha);
  38. mSurfaceLayer = mAnimLayer;
  39. mSurfaceControl.setLayer(mAnimLayer); //设置Z轴位置
  40. mSurfaceControl.setMatrix(
  41. mDsDx * w.mHScale, mDtDx * w.mVScale,
  42. mDsDy * w.mHScale, mDtDy * w.mVScale);
  43. ……
  44. } catch (RuntimeException e) {
  45. Slog.w(TAG, “Error updating surface in “ + w, e);
  46. if (!recoveringMemory) {
  47. mService.reclaimSomeSurfaceMemoryLocked( this, “update”, true);
  48. }
  49. }
  50. }
  51. }
  52. ……
  53. }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值