WindowManagerService(WMS)窗口显示流程分析之布局窗口

在上篇文章中分析了WMS显示流程中的添加窗口,本文将着重分析下布局窗口,即relayoutWindow流程。

本文以android 13代码为例进行源码分析。

经过之前的分析已经知道,在一个activity启动时,窗口显示的三部曲(Measure、layout、draw)的触发点都是在 ResumeActivityItem 事务执行到 ViewRootImpl::setView 方法触发的,我们就从 setView 开始进行分析。

1,在frameworks/base/core/java/android/view/ViewRootImpl.java中

java 复制代码
public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {
	
	final IWindowSession mWindowSession;//与WMS通信的binder
	final Choreographer mChoreographer;
	final W mWindow;
	View mView; //对应的DecorView
	
	public final Surface mSurface = new Surface(); // 应用端这个View树的 Surface,这个mSurface是专门提供给docerview上面的canvas. 真正的 Suface 创建是在 system_service 端触发
	private final SurfaceControl mSurfaceControl = new SurfaceControl();  // 对应的SurfaceControl

	public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,int userId) {
        synchronized (this) {
            if (mView == null) { // mView为null,表示这是第一次设置视图
			
                mView = view; // 将DecorView赋值给 mView
				
                requestLayout(); // 在添加到窗口管理器之前请求首次布局,确保在接收其他系统事件之前完成重新布局。relayoutWindow和finishDrawingWindow都在这里触发
				
                InputChannel inputChannel = null; // 用于窗口接收input
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { // 如果需要输入通道(没有设置INPUT_FEATURE_NO_INPUT_CHANNEL标志),则创建一个新的InputChannel。
                    inputChannel = new InputChannel();
                }
				
		     // 通过binder通信,调用wms的addWindow方法,addToDisplayAsUser的第一个参数mWindow在ViewRootImpl的构造函数中初始化即 mWindow = new W(this);
		     // 调用的 Session::addToDisplayAsUser 最终执行的是 WindowManagerService:: addWindow
                res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,
                        mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,
                        mTempControls); // 尝试将窗口添加到指定的显示和用户中。这一步涉及到多个参数,如窗口、窗口属性、可见性、显示ID、用户ID等
						
				view.assignParent(this); // 将视图(view)的父级设置为当前窗口(this)。DecorView::getParent 返回的是 ViewRootImpl 的原因
			}
		}
	}
}

上面的 setView 方法,执行了 requestLayout 方法,并进一步执行 scheduleTraversals 函数代码的目的是确保UI遍历(包括测量、布局和绘制)被调度执行。通过 Choreographer 来协调这个过程,并利用同步屏障来确保在遍历执行期间,消息队列中的其他同步消息不会干扰到遍历过程。

下面看下 requestLayout 函数。

scss 复制代码
@Override
	public void requestLayout() {
	    if (!mHandlingLayoutInLayoutRequest) {
	        checkThread(); //只有主线程才能更新UI
	        mLayoutRequested = true;
	        scheduleTraversals(); //调用scheduleTraversals函数
	    }
	}

上面的代码调用scheduleTraversals函数。

scss 复制代码
void scheduleTraversals() {
	    if (!mTraversalScheduled) {
	        mTraversalScheduled = true;
			  // 通过消息循环(Looper)的队列(Queue)发布一个同步屏障(Sync Barrier)。这个屏障用于阻塞消息队列中同步消息的处理,直到屏障被移除。mTraversalBarrier变量保存了这个屏障的引用。
	        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
			
			// 调用Choreographer对象的postCallback方法。Choreographer是Android系统中用于协调动画、输入和绘制的一个类。
			// 向Choreographer注册一个回调。CALLBACK_TRAVERSAL是回调的类型,表示这是一个遍历回调。
			// mTraversalRunnable是一个实现了遍历逻辑的可运行对象(Runnable),当回调被执行时,它会运行。
	        mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); // 执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable
			
	        notifyRendererOfFramePending(); // 通知渲染器有一帧即将被绘制。这通常是为了确保渲染器准备好处理新的帧。
	        pokeDrawLockIfNeeded(); // 根据需要戳一下绘制锁
	    }
	}

在上面的函数中执行 mTraversalRunnable,当下一个 VSync-app 到来的时候,会执行 TraversalRunnable 这个 Runnable.

scss 复制代码
final class TraversalRunnable implements Runnable {
	    @Override
	    public void run() {
	        doTraversal(); //在Runnable运行时调用doTravelsal,并最终调用到 performTraversals 函数
	    }
	}
	
	final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
	
	void doTraversal() {
	    if (mTraversalScheduled) {
	        mTraversalScheduled = false;
			// 通过消息循环(Looper)的队列(Queue)移除之前发布的同步屏障(Sync Barrier)。
			// 这个屏障是在scheduleTraversals方法中发布的,用于阻塞消息队列中同步消息的处理,直到遍历任务开始执行。现在遍历任务即将开始,所以移除这个屏障以允许其他同步消息继续被处理。
	        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障,那么 mHandler 就可以正常处理后面的消息了
	
	        if (mProfile) {
	            Debug.startMethodTracing("ViewAncestor");
	        }
	
	        performTraversals(); // 执行遍历任务。这个方法包含了测量、布局和绘制的实际逻辑
	
	        if (mProfile) {
	            Debug.stopMethodTracing();
	            mProfile = false;
	        }
	    }
	}

上面的代码调用 performTraversals 函数执行遍历任务。

ini 复制代码
private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
	private void performTraversals() {
	    // cache mView since it is used so much below...
	    final View host = mView; // 获取根视图(mView)并存储在局部变量host中
		
		// 调用relayoutWindow方法,传入参数、视图可见性和是否需要处理insets(内部空间),返回重新布局的结果。
	    relayoutResult = relayoutWindow(params, viewVisibility, insetsPending); //内部会将经过WMS计算后的窗口尺寸给mWinFrame,经过relayoutWindow 后就 View 就可以绘制了
		
		int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,lp.privateFlags);
		int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height,lp.privateFlags);
		
		performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); // 调用performMeasure方法,根据新的测量规格(childWidthMeasureSpec和childHeightMeasureSpec)来测量视图的大小。
		
		layoutRequested = true;
		final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
		
		if (didLayout) { // 如果请求了布局(layoutRequested为true)且视图没有被停止(mStopped为false)或报告下一个绘制(mReportNextDraw为true),则执行布局(performLayout)
	        performLayout(lp, mWidth, mHeight); //调用performLayout函数
		}
		
		boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
		if (!cancelAndRedraw) {
		    createSyncIfNeeded(); //设置了回调,等时机到了就会触发执行,而这个时机就是 View 绘制完成后,在 markSyncReady 触发,开始finish drawing流程
		}

		// 调用performDraw()尝试绘制,如果绘制失败且存在同步回调,则调用回调。
	    if (!performDraw() && mSyncBufferCallback != null) {
	        mSyncBufferCallback.onBufferReady(null);
	    }
		
		if (!cancelAndRedraw) {
		    mReportNextDraw = false;
		    mSyncBufferCallback = null;
		    mSyncBuffer = false;
		    if (isInLocalSync()) {
		        mSurfaceSyncer.markSyncReady(mSyncId); //触发绘制完成回调,markSyncReady 方法,最终会触发 ViewRootImpl::createSyncIfNeeded 方法下的 ViewRootImpl::reportDrawFinished 来真正 finishDrawingWindow 流程。
		        mSyncId = UNSET_SYNC_ID;
		    }
		}
	
	}

在 performTraversals 函数中分别执行了 relayoutWindow,performMeasure,performLayout,performDraw 等函数。并执行 createSyncIfNeeded 函数执行完成绘制。

首先分析下 relayoutWindow 函数。

java 复制代码
final IWindowSession mWindowSession; // 其初始化是在ViewRootImpl的构造函数中由传入的参数进行赋值
	public final Surface mSurface = new Surface(); // 应用端这个View树的 Surface,这个mSurface是专门提供给docerview上面的canvas.
	private final SurfaceControl mSurfaceControl = new SurfaceControl();  // 对应的SurfaceControl
	private final ClientWindowFrames mTmpFrames = new ClientWindowFrames(); // 临时保存最新的窗口信息
	final Rect mWinFrame; // frame given by window manager.当前窗口大小

	private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {
		mRelayoutRequested = true;
		int relayoutResult = 0;

		// 调用WMS的 relayoutWindow流程
        relayoutResult = mWindowSession.relayout(mWindow, params,
                requestedWidth, requestedHeight, viewVisibility,
                insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0,
                mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                mTempControls, mRelayoutBundle);
				
		if (!useBLAST()) {
            mSurface.copyFrom(mSurfaceControl);
        } else {
            updateBlastSurfaceIfNeeded(); // 给 mSurface 赋值,目前版本都走这
        }
		setFrame(mTmpFrames.frame); // 将WMS计算的窗口大小设置到当前
        return relayoutResult;
	}

上面的代码分别调用了 relayout、 updateBlastSurfaceIfNeeded、setFrame等函数。

通过 mWindowSession.relayout 调用WMS的 relayoutWindow 流程。 在调用 relayout 时,将 mTmpFrames 和 mSurfaceControl 作为参数传递了过去。 执行这个方法前 mSurfaceControl 只是一个没有实际内容的对象,但是经过 WMS::relayoutWindow 流程处理后,mSurfaceControl 就会真正持有一个 native 层的 Surface 句柄,有个这个 native 的 Surface 句柄,View 就可以把图像数据保存到Surface 中了。

通过调用 updateBlastSurfaceIfNeeded 函数将 mSurfaceControl 下的 surface 赋值给当前的变量 mSurface。

通过调用 setFrame 函数将WMS计算的窗口大小设置到当前,在执行了relayoutWindow 流程后 mTmpFrames 就有最新的尺寸信息了,需要赋值给真正保存窗口尺寸的变量 mWinFrame。

首先看下 ViewRootImpl::updateBlastSurfaceIfNeeded 函数。

scss 复制代码
private BLASTBufferQueue mBlastBufferQueue;
	void updateBlastSurfaceIfNeeded() {
		  // 经过system_service处理后的mSurfaceControl有值
        if (!mSurfaceControl.isValid()) {
	        return;
        }
    
        if (mBlastBufferQueue != null && mBlastBufferQueue.isSameSurfaceControl(mSurfaceControl)) {
            mBlastBufferQueue.update(mSurfaceControl,
                mSurfaceSize.x, mSurfaceSize.y,
                mWindowAttributes.format);
            return;
        }
    
        // If the SurfaceControl has been updated, destroy and recreate the BBQ to reset the BQ and
        // BBQ states.
        if (mBlastBufferQueue != null) {
            mBlastBufferQueue.destroy();
        }
    	  // 创建对象
        mBlastBufferQueue = new BLASTBufferQueue(mTag, mSurfaceControl,
                mSurfaceSize.x, mSurfaceSize.y, mWindowAttributes.format);
        mBlastBufferQueue.setTransactionHangCallback(sTransactionHangCallback);
		
        Surface blastSurface = mBlastBufferQueue.createSurface();
        // Only call transferFrom if the surface has changed to prevent inc the generation ID and
        // causing EGL resources to be recreated.
		// 给当前mSurface赋值
        mSurface.transferFrom(blastSurface);
    }

上面的代码调用了 createSurface 函数。这个放在后面分析,先分析下 relayoutWindow 流程。

在 relayoutWindow 函数中 mWindowSession.relayout 调用WMS的 relayoutWindow 流程。其中 mWindowSession 是这样定义的,即 final IWindowSession mWindowSession; 其初始化是在ViewRootImpl的构造函数中由传入的参数进行赋值。

2, 在frameworks/base/service/core/java/com/android/server/wm/Session.java中

scala 复制代码
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
	final WindowManagerService mService;
	
	@Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Bundle outSyncSeqIdBundle) {
        if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from "
                + Binder.getCallingPid());
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag);
		//调用wms的 relayoutWindow 函数
        int res = mService.relayoutWindow(this, window, attrs,
                requestedWidth, requestedHeight, viewFlags, flags,
                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSyncSeqIdBundle);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to "
                + Binder.getCallingPid());
        return res;
    }
}

上面的代码中,在 Session::relayout 方法的参数中应用端传过来的 mSurfaceControl 变成了:outSurfaceControl,说明这是个出参会在 WindowManagerService::relayoutWindow 方法对其进行真正的赋值。 上面的函数 relayout 调用WMS的 relayoutWindow 函数。

3,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中

java 复制代码
final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();
	final WindowSurfacePlacer mWindowPlacerLocked; // 在WindowManagerService的构造函数中执行mWindowPlacerLocked = new WindowSurfacePlacer(this);进行初始化。

	public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,int requestedWidth, int requestedHeight, int viewVisibility, int flags,ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
		synchronized (mGlobalLock) {
			  // 从mWindowMap中获取WindowState
            final WindowState win = windowForClientLocked(session, client, false); // 调用windowForClientLocked方法,根据传入的session和client参数获取对应的WindowState对象。如果没有找到对应的窗口,win将为null
			
			// 获取WindowState对象对应的DisplayContent和DisplayPolicy。DisplayContent代表一个显示内容,而DisplayPolicy则包含与显示相关的策略或行为。
            final DisplayContent displayContent = win.getDisplayContent();
            final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
            
            WindowStateAnimator winAnimator = win.mWinAnimator; // 获取WindowState的WindowStateAnimator对象,该对象负责窗口的动画效果
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight); // 把应用端请求的大小,保存到WindowState下
            }
			
			if (attrs != null) {
                displayPolicy.adjustWindowParamsLw(win, attrs); // 调用adjustWindowParamsLw方法调整窗口参数。
			}
			
			win.setViewVisibility(viewVisibility); // 设置窗口可见 viewVisibility = VISIBLE
			
			final boolean shouldRelayout = viewVisibility == View.VISIBLE &&
                    (win.mActivityRecord == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
                            || win.mActivityRecord.isClientVisible());
			if (shouldRelayout) {
		        try {
		    		  // 创建SurfaceControl(重点)
		            result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); //创建Buff类型的Surface
		        } catch (Exception e) {
		            displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
		    
		            ProtoLog.w(WM_ERROR,
		                    "Exception thrown when creating surface for client %s (%s). %s",
		                    client, win.mAttrs.getTitle(), e);
		            Binder.restoreCallingIdentity(origId);
		            return 0;
		        }
		    }
			
			mWindowPlacerLocked.performSurfacePlacement(true /* force */); // 计算窗口大小(极其重要的方法),窗口的摆放 (View一般有变化也要执行 layout,WMS在管理窗口这边肯定也要执行layout)
			
			if (focusMayChange) {
		    	  // 更新焦点
		        if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
		            imMayMove = false;
		        }
		    }
			
			// 填充WMS计算好后的数据,返回应用端
		    win.fillClientWindowFramesAndConfiguration(outFrames, mergedConfiguration,
		            false /* useLatestConfig */, shouldRelayout); // 将计算好的窗口尺寸返回给应用端
		}
		return result;
	}

上面的代码调用了 windowForClientLocked 获取Window.

java 复制代码
final WindowState windowForClientLocked(Session session, IBinder client, boolean throwOnError) {
		WindowState win = mWindowMap.get(client);
		return win;
	}

上面的 relayoutWindow 函数主要执行了下面三个函数,

createSurfaceControl: 创建"Buff"类型的Surface;

performSurfacePlacement: 窗口的摆放 (View一般有变化也要执行 layout,WMS在管理窗口这边肯定也要执行layout);

fillClientWindowFramesAndConfiguration :将计算好的窗口尺寸返回给应用端。 首先分析下 createSurfaceControl 函数。

createSurfaceControl函数第一个参数outSurfaceControl: WMS 创建好一个 Surface 后,还需要返回给应用端用于 View 的绘制,就是通过这个参数,由参数命名也可以知道这是一个"出参"。

第二个参数result:方法执行结果。

第三个参数win:当前窗口对应的WindowState,稍后创建Surface会挂载到这个WindowState节点之下.

第四个参数winAnimator:WindowStateAnimator对象,管理窗口状态和动画,稍后通过其内部方法创建Surface。

csharp 复制代码
private int createSurfaceControl(SurfaceControl outSurfaceControl, int result,
	        WindowState win, WindowStateAnimator winAnimator) {
	    if (!win.mHasSurface) {
	        result |= RELAYOUT_RES_SURFACE_CHANGED;
	    }
	
	    WindowSurfaceController surfaceController; // 创建WindowSurfaceController对象
	    try {
	        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createSurfaceControl");
			
	        surfaceController = winAnimator.createSurfaceLocked(); // 创建"Buff"类型Surface
			
	    } finally {
	        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
	    }
	    if (surfaceController != null) {
	        surfaceController.getSurfaceControl(outSurfaceControl); // 出参给应用端,WMS 这边创建好后的 Surface 设置给 relayoutWindow 的参数 outSurfaceControl 。这样一来应用端就有了可以保持绘制数据的 Surface ,然后就可以执行 View 树的绘制了。
	        ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl);
	
	    } else {
	        // For some reason there isn't a surface.  Clear the
	        // caller's object so they see the same state.
	        ProtoLog.w(WM_ERROR, "Failed to create surface control for %s", win);
	        outSurfaceControl.release();
	    }
	
	    return result;
	}

上面的函数中调用 createSurfaceLocked 函数。

4,在frameworks/base/services/core/java/com/android/server/wm/WindowStateAnimator.java中

java 复制代码
class WindowStateAnimator {

	final WindowState mWin; //在构造函数 WindowStateAnimator(final WindowState win){}中被赋值。
	WindowSurfaceController mSurfaceController;
	int mDrawState;
	
	WindowSurfaceController createSurfaceLocked() {
	    final WindowState w = mWin;
	
        if (mSurfaceController != null) {
            return mSurfaceController;
        }
		w.setHasSurface(false);
		
		resetDrawState(); // 重置窗口状态  -- DRAW_PENDING
		
		// 创建WindowSurfaceController
        mSurfaceController = new WindowSurfaceController(attrs.getTitle().toString(), format,
                flags, this, attrs.type);
		w.setHasSurface(true);
				
		return mSurfaceController;
	}
	
	//WindowState 状态是保存在 WindowStateAnimator 中
	void resetDrawState() {
        mDrawState = DRAW_PENDING; // 将 WindowState 状态设置为 DRAW_PENDING 表示等待绘制
    
        if (mWin.mActivityRecord == null) {
            return;
        }
    
        if (!mWin.mActivityRecord.isAnimating(TRANSITION)) {
            mWin.mActivityRecord.clearAllDrawn();
        }
    }
}

上面的代码在 createSurfaceLocked 函数中创建 WindowSurfaceController 对象。

5,在frameworks/base/services/core/java/com/android/server/wm/WindowSurfaceController.java中

scss 复制代码
class WindowSurfaceController {
	SurfaceControl mSurfaceControl;
	final WindowStateAnimator mAnimator;
	private final WindowManagerService mService;
	
	WindowSurfaceController(String name, int format, int flags, WindowStateAnimator animator,int windowType) {
        mAnimator = animator;
        title = name;
        mService = animator.mService;
        final WindowState win = animator.mWin; // 拿到WindowState
        mWindowType = windowType;
        mWindowSession = win.mSession;
    
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
		
		// 通过 makeSurface() 方法构建Surface 对象
        final SurfaceControl.Builder b = win.makeSurface() 
                .setParent(win.getSurfaceControl()) // 获取到 WindowState 对象,设置为创建 Surface 的父节点
                .setName(name) // 第一个参数传递的字符串name最终也会作为 Surface 的 name
                .setFormat(format)
                .setFlags(flags)
                .setMetadata(METADATA_WINDOW_TYPE, windowType)
                .setMetadata(METADATA_OWNER_UID, mWindowSession.mUid)
                .setMetadata(METADATA_OWNER_PID, mWindowSession.mPid)
                .setCallsite("WindowSurfaceController");
    
         final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags
                 & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0);
    
    	 // 高版本都为BLAST
         if (useBLAST) {
             b.setBLASTLayer(); // 设置为"Buff"图层
         }
    
		 // 触发build
         mSurfaceControl = b.build(); // build 出一个 Surface 
    
         Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
     }
	 
	 void getSurfaceControl(SurfaceControl outSurfaceControl) {
	 	 // 将framework层的SurfaceControl copy给应用层传递过来的outSurfaceControl
	     outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
	 }
	 
	 
	 void setSecure(boolean isSecure) {
	     ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title);
	 
	     if (mSurfaceControl == null) {
	         return;
	     }
	     if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setSecureLocked");
	     mService.openSurfaceTransaction();
	     try {
	         getGlobalTransaction().setSecure(mSurfaceControl, isSecure);
	 
	         final DisplayContent dc = mAnimator.mWin.mDisplayContent;
	         if (dc != null) {
	             dc.refreshImeSecureFlag(getGlobalTransaction());
	         }
	     } finally {
	         mService.closeSurfaceTransaction("setSecure");
	         if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION setSecureLocked");
         }
     }
}

在上面的代码中,通过 makeSurface() 方法构建Surface 对象。在Android系统中,所有图像的绘制都需要渲染到Surface上,而Surface的本质是一块渲染缓存,即GraphicBuffer。 分析到这里,surface 就完成了创建。

在WMS的 relayoutWindow 函数中,通过调用 createSurfaceControl 并进一步调用 createSurfaceLocked 以及后续等步骤创建完成 surface 后,需要设置给应用端,即在 relayoutWindow 函数中的传递的参数 outSurfaceControl. 这一步在 WindowManagerService::createSurfaceControl 放中执行 WindowSurfaceController::getSurfaceControl 时完成。

javascript 复制代码
void getSurfaceControl(SurfaceControl outSurfaceControl) {
	 // 将framework层的SurfaceControl copy给应用层传递过来的outSurfaceControl
	 outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl");
}

这样一来应用端就有了可以保持绘制数据的 Surface ,然后就可以执行 View 树的绘制了。

上面分析了relayoutWindow函数中的 createSurfaceControl 函数,下面分析下 performSurfacePlacement 函数。

在WMS的relayoutWindow函数中调用了mWindowPlacerLocked.performSurfacePlacement(true /* force */),此时走到WindowSurfacePlacer.java中.

performSurfacePlacement 方法的调用链如下:

arduino 复制代码
WindowSurfacePlacer::performSurfacePlacement
    WindowSurfacePlacer::performSurfacePlacementLoop
        RootWindowContainer::performSurfacePlacement
            RootWindowContainer::performSurfacePlacementNoTrace
                RootWindowContainer::applySurfaceChangesTransaction
                    DisplayContent::applySurfaceChangesTransaction
                        DisplayContent::performLayout 
                            DisplayContent::performLayoutNoTrace
                                DisplayContent::mPerformLayout 
                                    DisplayPolicy::layoutWindowLw 
                                        WindowLayout::computeFrames   -- 计算窗口大小,保存在 sTmpClientFrames中
                                        WindowState::setFrames        -- 将计算结果 sTmpClientFrames 的数据设置给窗口

6,在frameworks/base/services/core/java/com/android/server/wm/WindowSurfacePlacer.java中

ini 复制代码
class WindowSurfacePlacer {
	
	final void performSurfacePlacement(boolean force) { //在wms中调用时传递的参数是true
        if (mDeferDepth > 0 && !force) {
            mDeferredRequests++;
            return;
        }
        int loopCount = 6; // 最大次数循环为6次
        do {
            mTraversalScheduled = false;
            performSurfacePlacementLoop(); // 调用 performSurfacePlacementLoop 函数
            mService.mAnimationHandler.removeCallbacks(mPerformSurfacePlacement);
            loopCount--;
        } while (mTraversalScheduled && loopCount > 0);
        mService.mRoot.mWallpaperActionPending = false;
    }
}

上面的函数调用 performSurfacePlacementLoop 函数。

java 复制代码
private void performSurfacePlacementLoop() {
	
		// 执行 RootWindowContainer::performSurfacePlacement 这个代表对屏幕进行一次 layout
        //在WMS中有 RootWindowContainer mRoot; // The root of the device window hierarchy.
		// 对所有窗口执行布局操作
        mService.mRoot.performSurfacePlacement(); 
        
        mInLayout = false; // 布局完成
		
		if (mService.mRoot.isLayoutNeeded()) {
            if (++mLayoutRepeatCount < 6) {
				// 布局次数小于6次,则需要再次请求布局.
                requestTraversal();
            } else {
                Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
                mLayoutRepeatCount = 0;
            }
        } else {
	        mLayoutRepeatCount = 0;
        }
	}
	
	void requestTraversal() {
	    if (mTraversalScheduled) {
	        return;
	    }
	
	    // Set as scheduled even the request will be deferred because mDeferredRequests is also
	    // increased, then the end of deferring will perform the request.
	    mTraversalScheduled = true;
	    if (mDeferDepth > 0) {
	        mDeferredRequests++;
	        if (DEBUG) Slog.i(TAG, "Defer requestTraversal " + Debug.getCallers(3));
	        return;
	    }
	    mService.mAnimationHandler.post(mPerformSurfacePlacement); // 通过Handler触发
	}
	
	private class Traverser implements Runnable {
	    @Override
	    public void run() {
	        synchronized (mService.mGlobalLock) {
	            performSurfacePlacement();
	        }
	    }
	}
	
	private final Traverser mPerformSurfacePlacement = new Traverser();
	
	WindowSurfacePlacer(WindowManagerService service) {
        mService = service;
    }

在上面的代码中 performSurfacePlacementLoop 通过语句 mService.mRoot.performSurfacePlacement()调用了 RootWindowContainer 中的 performSurfacePlacement 函数。

7,在frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java中

scala 复制代码
class RootWindowContainer extends WindowContainer<DisplayContent> implements DisplayManager.DisplayListener {

	void performSurfacePlacement() {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performSurfacePlacement");
        try {
            performSurfacePlacementNoTrace(); //调用performSurfacePlacementNoTrace函数
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }
	
	void performSurfacePlacementNoTrace() {
		if (mWmService.mFocusMayChange) {  // 如果需要,则更新焦点
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(
                    UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
        }
		
		Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applySurfaceChanges");
        // 开启事务,在WindowContainer.java中定义有mWmService对象,即protected final WindowManagerService mWmService;
        mWmService.openSurfaceTransaction(); //调用openSurfaceTransaction函数,开启Surface事务。
        try {
        	 // 处理事务(执行窗口尺寸计算,surface状态变更等操作),处理Surface事务
            applySurfaceChangesTransaction();
        } catch (RuntimeException e) {
            Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
        } finally {
        	 // 关闭事务,做事务提交
            mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            if (SHOW_LIGHT_TRANSACTIONS) {
                Slog.i(TAG,
                        "<<< CLOSE TRANSACTION performLayoutAndPlaceSurfaces");
            }
        }
		
		checkAppTransitionReady(surfacePlacer); // Activity 切换事务处理,条件满足也会将窗口状态设置为HAS_DRAW 流程
		
		if (mWmService.mFocusMayChange) { // 再次判断是否需要处理焦点变化
            mWmService.mFocusMayChange = false;
            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                    false /*updateInputWindows*/);
        }
		handleResizingWindows(); // 如果过程中size或者位置变化,则通知客户端重新relayout
		
		// Destroy the surface of any windows that are no longer visible.
        i = mWmService.mDestroySurface.size(); // 销毁不可见的窗口
	}

上面函数中调用了 applySurfaceChangesTransaction 函数。

ini 复制代码
private void applySurfaceChangesTransaction() {
		final int count = mChildren.size(); // 正常情况就一个屏幕
        for (int j = 0; j < count; ++j) { // 遍历每个屏幕执行 DisplayContent::applySurfaceChangesTransaction
            final DisplayContent dc = mChildren.get(j);
            dc.applySurfaceChangesTransaction();
        }
	}

在上面的 performSurfacePlacement 函数中调用了 performSurfacePlacementNoTrace 函数。

在函数 performSurfacePlacementNoTrace 中,先后调用了 openSurfaceTransaction、applySurfaceChangesTransaction、closeSurfaceTransaction、checkAppTransitionReady 函数等。

先分析下 openSurfaceTransaction 函数,通过 mWmService.openSurfaceTransaction()被调用,其中在WindowContainer.java中定义有mWmService对象,即protected final WindowManagerService mWmService;

8,在frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java中

csharp 复制代码
void openSurfaceTransaction() {
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
            SurfaceControl.openTransaction();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
    }

9,在frameworks/base/core/java/android/view/SurfaceControl.java中

typescript 复制代码
public final class SurfaceControl implements Parcelable {
	static GlobalTransactionWrapper sGlobalTransaction;
	static long sTransactionNestCount = 0;
	
	public static void openTransaction() { // 在WMS中被调用
        synchronized (SurfaceControl.class) {
            if (sGlobalTransaction == null) {
                sGlobalTransaction = new GlobalTransactionWrapper();
            }
            synchronized(SurfaceControl.class) {
                sTransactionNestCount++;
            }
        }
    }
	
	public static void closeTransaction() {
	    synchronized(SurfaceControl.class) {
	        if (sTransactionNestCount == 0) {
	            Log.e(TAG,
	                    "Call to SurfaceControl.closeTransaction without matching openTransaction");
	        } else if (--sTransactionNestCount > 0) {
	            return;
	        }
	        sGlobalTransaction.applyGlobalTransaction(false);
	    }
	}
	
	private static class GlobalTransactionWrapper extends SurfaceControl.Transaction { // SurfaceControl.Transaction是一个用于控制屏幕显示内容的类,通常用于在Android系统中进行底层UI操作
	    void applyGlobalTransaction(boolean sync) {
	        applyResizedSurfaces();
	        notifyReparentedSurfaces();
	        nativeApplyTransaction(mNativeObject, sync); // 用于实际将事务应用到系统底层
	    }
	
	    @Override
	    public void apply(boolean sync) { // 被重写以抛出一个RuntimeException异常,表明全局事务必须通过closeTransaction方法来应用,而不是直接调用apply方法。
	        throw new RuntimeException("Global transaction must be applied from closeTransaction");
	    }
	}
	
	public static class Transaction implements Closeable, Parcelable { ... }
	
}

在分析了 openSurfaceTransaction 后,接着分析 applySurfaceChangesTransaction 函数。

在 RootWindowContainer 函数中,applySurfaceChangesTransaction 函数如下:

ini 复制代码
private void applySurfaceChangesTransaction() {
		final int count = mChildren.size(); // 正常情况就一个屏幕
        for (int j = 0; j < count; ++j) { // 遍历每个屏幕执行 DisplayContent::applySurfaceChangesTransaction
            final DisplayContent dc = mChildren.get(j);
            dc.applySurfaceChangesTransaction();
        }
	}

上面的代码中 applySurfaceChangesTransaction 函数调用了DisplayContent类的 applySurfaceChangesTransaction 函数。

10,在frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java中

java 复制代码
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

	private final DisplayPolicy mDisplayPolicy; //在DisplayContent的构造函数中定义,即mDisplayPolicy = new DisplayPolicy(mWmService, this);
	private boolean mLayoutNeeded;
	
	void applySurfaceChangesTransaction() {
        final WindowSurfacePlacer surfacePlacer = mWmService.mWindowPlacerLocked;
    
        mTmpUpdateAllDrawn.clear(); // 置空,新的执行,清除数据
		
		// Perform a layout, if needed.
        performLayout(true /* initial */, false /* updateInputWindows */); // 执行布局,该方法最终会调用performLayoutNoTrace,计算窗口的布局参数。layoutWindow 流程。
		
		Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyPostLayoutPolicy");
        try {
            mDisplayPolicy.beginPostLayoutPolicyLw();
        	  // 对所有窗口执行布局策略,遍历所有窗口执行 lambda表达式。
            forAllWindows(mApplyPostLayoutPolicy, true /* traverseTopToBottom */);
            mDisplayPolicy.finishPostLayoutPolicyLw();
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
		
		Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
        try {
        	  // 遍历所有窗口,主要是改变窗口状态设置为READY_TO_SHOW,当前逻辑不满足,不会执行最终设置
			  // 执行 mApplySurfaceChangesTransaction 这个 lambda表达式 ,当前分析的场景是会把目标窗口状态设置为 READY_TO_SHOW 和 HAS_DRAWN
            forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        }
		
        // finishDrawing()流程,条件满足触发提交Surface到SurfaceFlinger 
        prepareSurfaces(); // Surface 操作
	}
	在上面的函数 applySurfaceChangesTransaction 中,调用了 
	
	void performLayout(boolean initial, boolean updateInputWindows) {
	    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "performLayout");
	    try {
	        performLayoutNoTrace(initial, updateInputWindows); //调用 performLayoutNoTrace 函数
	    } finally {
	        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
	    }
	}
	
	private void performLayoutNoTrace(boolean initial, boolean updateInputWindows) {
	    if (!isLayoutNeeded()) {
	        return;
	    }
	    clearLayoutNeeded(); // 将mLayoutNeeded设置为flase
		
		forAllWindows(mPerformLayout, true /* traverseTopToBottom */); // 对所有顶级窗口进行布局
		
		forAllWindows(mPerformLayoutAttached, true /* traverseTopToBottom */); // 处理子窗口的布局
	}
	
	private final Consumer<WindowState> mApplySurfaceChangesTransaction = w -> {
		
		if (w.mHasSurface) {
	        // Take care of the window being ready to display.
	        final boolean committed = winAnimator.commitFinishDrawingLocked(); // 调用 commitFinishDrawingLocked 函数
		}
		
		// allDrawn相关逻辑
	    final ActivityRecord activity = w.mActivityRecord;
	    if (activity != null && activity.isVisibleRequested()) {
	        activity.updateLetterboxSurface(w);
	    	  // 更新绘制状态
	        final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
	        if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
	            mTmpUpdateAllDrawn.add(activity); // 加入集合
	        }
	    }
	};
	
	
	private final Consumer<WindowState> mPerformLayout = w -> {
	    if (w.mLayoutAttached) { // 如果当前窗口为子窗口则直接返回
	        return;
	    }
		final boolean gone = w.isGoneForLayout(); // 先判断当前窗口是否会不可见
		if (!gone || !w.mHaveFrame || w.mLayoutNeeded) { // 如果窗口不是不可见的,或者窗口没有框架,或者窗口需要布局
			// 调用DisplayPolicy::layoutWindowLw
	        getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
		}
	};
	
	private final Consumer<WindowState> mPerformLayoutAttached = w -> {
	    if (!w.mLayoutAttached) { // 如果不是子窗口则返回
	        return;
	    }
		if ((w.mViewVisibility != GONE && w.mRelayoutCalled) || !w.mHaveFrame || w.mLayoutNeeded) {
			getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames); // 调用 layoutWindowLw 函数
		}
	}
	
	private void clearLayoutNeeded() {
	    if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
	    mLayoutNeeded = false;
	}
	
	boolean isLayoutNeeded() {
	    return mLayoutNeeded;
	}
	
}

上面的代码中调用到了 DisplayPolicy类的 layoutWindowLw 函数。

11,在frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java中

java 复制代码
public class DisplayPolicy {
	
	private final WindowLayout mWindowLayout = new WindowLayout();
	
	public void layoutWindowLw(WindowState win, WindowState attached, DisplayFrames displayFrames) {
	    if (win.skipLayout()) { // 判断是否需要跳过布局
	        return;
	    }
	
	    // This window might be in the simulated environment.
	    // We invoke this to get the proper DisplayFrames.
	    displayFrames = win.getDisplayFrames(displayFrames); // 获取DisplayFrames
	
		  // 获取某个方向的窗口布局参数
	    final WindowManager.LayoutParams attrs = win.getLayoutingAttrs(displayFrames.mRotation);
	    final Rect attachedWindowFrame = attached != null ? attached.getFrame() : null;
	
	    // If this window has different LayoutParams for rotations, we cannot trust its requested
	    // size. Because it might have not sent its requested size for the new rotation.
	    final boolean trustedSize = attrs == win.mAttrs;
		  // 应用端请求的宽高信息
	    final int requestedWidth = trustedSize ? win.mRequestedWidth : UNSPECIFIED_LENGTH;
	    final int requestedHeight = trustedSize ? win.mRequestedHeight : UNSPECIFIED_LENGTH;
	
		  // 调用WindowLayout.computeFrames计算窗口布局大小
	    mWindowLayout.computeFrames(attrs, win.getInsetsState(), displayFrames.mDisplayCutoutSafe,
	            win.getBounds(), win.getWindowingMode(), requestedWidth, requestedHeight,
	            win.getRequestedVisibilities(), attachedWindowFrame, win.mGlobalScale,
	            sTmpClientFrames);
		  // 将计算的布局参数赋值给windowFrames
	    win.setFrames(sTmpClientFrames, win.mRequestedWidth, win.mRequestedHeight);
	}
}

上面的代码中,WindowLayout::computeFrames 计算窗口大小的,有一套计算规则。WindowState::setFrames 然后将计算好后的大小设置给 WindowState。

12, 在frameworks/base/core/java/android/view/WindowLayout.java中

typescript 复制代码
public class WindowLayout {
	public void computeFrames(...) {}
}

13, 在frameworks/base/services/core/java/com/android/server/wm/WindowState.java中

ini 复制代码
class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,InsetsControlTarget, InputTarget {

	private final WindowFrames mWindowFrames = new WindowFrames();
	
	void setFrames(ClientWindowFrames clientWindowFrames, int requestedWidth, int requestedHeight) {
    final WindowFrames windowFrames = mWindowFrames; // 获取当前窗口的框架信息实例
    mTmpRect.set(windowFrames.mParentFrame); // 将父窗口框架信息复制到临时变量 mTmpRect

    if (LOCAL_LAYOUT) {
        windowFrames.mCompatFrame.set(clientWindowFrames.frame);

        windowFrames.mFrame.set(clientWindowFrames.frame);
        windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
        windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
        if (hasCompatScale()) {
            // The frames sent from the client need to be adjusted to the real coordinate space.
            windowFrames.mFrame.scale(mGlobalScale);
            windowFrames.mDisplayFrame.scale(mGlobalScale);
            windowFrames.mParentFrame.scale(mGlobalScale);
        }
    } else {
        windowFrames.mDisplayFrame.set(clientWindowFrames.displayFrame);
        windowFrames.mParentFrame.set(clientWindowFrames.parentFrame);
        windowFrames.mFrame.set(clientWindowFrames.frame);

        windowFrames.mCompatFrame.set(windowFrames.mFrame); // 设置兼容框架信息与窗口框架相同
        if (hasCompatScale()) {
            // Also, the scaled frame that we report to the app needs to be adjusted to be in
            // its coordinate space.
            windowFrames.mCompatFrame.scale(mInvGlobalScale); // 对windowFrames.mCompatFrame缩放,确保在应用坐标空间内正确展示
        }
    }
    windowFrames.setParentFrameWasClippedByDisplayCutout(
            clientWindowFrames.isParentFrameClippedByDisplayCutout);

    // Calculate relative frame
    windowFrames.mRelFrame.set(windowFrames.mFrame); // 计算相对框架信息
    WindowContainer<?> parent = getParent(); // 将窗口框架转换为相对于父容器的坐标系
    int parentLeft = 0;
    int parentTop = 0;
    if (mIsChildWindow) { // 如果当前窗口是子窗口,则根据其父窗口计算偏移量
        parentLeft = ((WindowState) parent).mWindowFrames.mFrame.left;
        parentTop = ((WindowState) parent).mWindowFrames.mFrame.top;
    } else if (parent != null) { // 若当前窗口不是子窗口但有父容器,则从父容器边界获取偏移量
        final Rect parentBounds = parent.getBounds();
        parentLeft = parentBounds.left;
        parentTop = parentBounds.top;
    }
    windowFrames.mRelFrame.offsetTo(windowFrames.mFrame.left - parentLeft,
            windowFrames.mFrame.top - parentTop);

	  // 检查请求的宽高是否改变,以及父窗口框架是否有变化
    if (requestedWidth != mLastRequestedWidth || requestedHeight != mLastRequestedHeight
            || !mTmpRect.equals(windowFrames.mParentFrame)) {
		  // 更新最后请求的宽高值,并标记内容发生变化
        mLastRequestedWidth = requestedWidth;
        mLastRequestedHeight = requestedHeight;
        windowFrames.setContentChanged(true);
    }
      if (mAttrs.type == TYPE_DOCK_DIVIDER) { // 如果窗口类型为 DOCK_DIVIDER 类型,比如分屏应用中间的分割线是这个类型
          if (!windowFrames.mFrame.equals(windowFrames.mLastFrame)) {
              mMovedByResize = true;
          }
      }

      if (mIsWallpaper) { // 如果当前窗口是壁纸
          final Rect lastFrame = windowFrames.mLastFrame;
          final Rect frame = windowFrames.mFrame;
          if (lastFrame.width() != frame.width() || lastFrame.height() != frame.height()) {
              mDisplayContent.mWallpaperController.updateWallpaperOffset(this, false /* sync */);
          }
      }

      updateSourceFrame(windowFrames.mFrame); // 更新源框架信息

      if (LOCAL_LAYOUT) {
          if (!mHaveFrame) {
              // The first frame should not be considered as moved.
              updateLastFrames();
          }
      }

      if (mActivityRecord != null && !mIsChildWindow) {
          mActivityRecord.layoutLetterbox(this);
      }
      mSurfacePlacementNeeded = true;
      mHaveFrame = true;
	}
}

经过 WindowState::setFrames 方法后,窗口自己就有了一个确定的尺寸了,但是绘制内容是在应用端,所以现在需要把这个窗口尺寸传递给应用端。

在WindowManagerService::relayoutWindow 方法中知道,经过 WindowSurfacePlacer::performSurfacePlacement 方法计算出窗口尺寸后, 会执行 WindowState::fillClientWindowFramesAndConfiguration 方法就尺寸信息填充到参数 outFrames 中,也就是传递给应用端。

14, 在frameworks/base/services/core/java/com/android/server/wm/WindowState.java中

typescript 复制代码
void fillClientWindowFramesAndConfiguration(ClientWindowFrames outFrames,
	        MergedConfiguration outMergedConfiguration, boolean useLatestConfig,
	        boolean relayoutVisible) {
		  // 尺寸信息设置给应用端
	    outFrames.frame.set(mWindowFrames.mCompatFrame);
	    outFrames.displayFrame.set(mWindowFrames.mDisplayFrame);
		
		mLastConfigReportedToClient = true; // 标记已向客户端报告过配置信息
	}

上面代码中这个 outFrames 就是应用端 ViewRootImpl执行 relayout 方法触发WMS::relayoutWindow 传递的参数 mTmpFrames。 上面分析了 relayoutWindow 函数的 createSurfaceControl、performSurfacePlacement、fillClientWindowFramesAndConfiguration 等函数。

至此,relayoutWindow 的流程分析完了。

相关推荐
QEasyCloud20221 小时前
如何将钉钉付款单数据集成到MySQL数据库
android
西瓜本瓜@2 小时前
在Android开发中实现静默拍视频
android·java·开发语言·学习·音视频
Yang_Mao_Shan4 小时前
Android启动流程_SystemServer阶段
android
会飞的土拨鼠呀4 小时前
Centos7.9安装MySQL(二进制)
android·数据库·mysql
深圳之光4 小时前
android 12 禁止三方APP 使用API 直接打开wifi的修改方法
android
小羊子说8 小时前
智能座舱相关术语全解及多模态交互在智能座舱中的应用
android·车载系统·汽车·交互
wrx繁星点点9 小时前
桥接模式:解耦抽象与实现的利器
android·java·开发语言·jvm·spring cloud·intellij-idea·桥接模式
镰刀出海10 小时前
RN开发环境配置与Android版本app运行
android·react native
Python大数据分析11 小时前
JetPack Compose安卓开发之底部导航Tabbar
android·vue.js·elementui
前期后期11 小时前
Android 在github网站下载项目:各种很慢怎么办?比如gradle下载慢;访问github慢;依赖下载慢
android·github