framework——View新增過程

little_fat_sheep發表於2020-12-14

1 前言

WMS啟動流程 中介紹了 WindowManagerService 的啟動流程,本文將介紹 View 的新增流程,一共分為2步:

  1. 在應用程式程式中新增 View
  2. 在系統(system_server)程式中新增 View

如圖,淺藍色的類是應用程式程式中執行的,深藍色的類是在系統程式中執行的,黃色的類是指 AIDL 檔案生成的介面(用於跨程式)。

2 View 新增過程

2.1 應用程式程式中 View 的新增

(1)step 1:addView()

/frameworks/base/core/java/android/view/WindowManagerImpl.java 

public void addView(View view, ViewGroup.LayoutParams params) {
	applyDefaultToken(params);
	mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

(2)step 2:addView()

 /frameworks/base/core/java/android/view/WindowManagerGlobal.java

public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
	...
	final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
	...

	ViewRootImpl root;
	View panelParentView = null; //父視窗的 Decor(根) View

	synchronized (mLock) {
		...
		//如果是子視窗,則通過 token 尋找父視窗的 Decor(根) View
		if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
			final int count = mViews.size();
			for (int i = 0; i < count; i++) {
				if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
					panelParentView = mViews.get(i); //mViews = new ArrayList<View>(),儲存了所有視窗的 Decor(根) View
				}
			}
		}
		//每個視窗對應一個 ViewRootImpl 物件
		root = new ViewRootImpl(view.getContext(), display);
		view.setLayoutParams(wparams);
		//存留 view、root、params資訊
		mViews.add(view);
		mRoots.add(root);
		mParams.add(wparams);
		try {
			root.setView(view, wparams, panelParentView);
		}
		...
	}
}

可以看到,每個 Decor(根) View,都與一個 LayoutParams 和一個 ViewRootImpl 一一對應

(3)step 3:setView()

/frameworks/base/core/java/android/view/ViewRootImpl.java

在 WindowManagerGlobal 的 addView() 方法中建立了 ViewRootImpl 物件,因此,先看看 ViewRootImpl 的構造方法。

public ViewRootImpl(Context context, Display display) {
	mContext = context;
    //IWindowSession 型別,單例物件,getWindowSession() 方法呼叫 WMS 的 openSession() 方法,new 一個 Session 物件
	mWindowSession = WindowManagerGlobal.getWindowSession(); 
	mDisplay = display;
	...
	mWindow = new W(this); //IWindow.Stub 的實現類
	...
    mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this, context); //View.AttachInfo 型別
    ...
	mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
    ...
}

注意:在建立 ViewRootImpl 時,也建立了 W,進一步說明每個 Decor(根) View 都與一個 W 一一對應,即 mWindow.asBinder() 可以作為 Decor(根)View 的身份標識

再看看 ViewRootImpl 的 setView 方法。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
	synchronized (this) {
		if (mView == null) {
			mView = view;
            ...
			mWindowAttributes.copyFrom(attrs);
			...
			mAttachInfo.mRootView = view;
			...
			if (panelParentView != null) {
				mAttachInfo.mPanelParentWindowToken = panelParentView.getApplicationWindowToken();
			}
            ...
			if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
				mInputChannel = new InputChannel();
			}
			...
			try {
				...
				res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes, getHostVisibility(), 
					mDisplay.getDisplayId(), mTmpFrame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, 
					mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel, mTempInsets);
				setFrame(mTmpFrame);
			}
			...
		}
	}
}

2.2 系統程式中 View 的新增

(1)step 1:Session 的建立過程

 在 ViewRootImpl 的構造方法中,呼叫了 WindowManagerGlobal 的 getWindowSession() 方法中獲取了 Session 物件,因此,先看看 Session 物件的建立過程。

WindowManagerGlobal.java

public static IWindowSession getWindowSession() {
	synchronized (WindowManagerGlobal.class) {
		if (sWindowSession == null) {
			try {
				...
				IWindowManager windowManager = getWindowManagerService(); //獲取 WMS
				sWindowSession = windowManager.openSession(
						new IWindowSessionCallback.Stub() {
							@Override
							public void onAnimatorScaleChanged(float scale) {
								ValueAnimator.setDurationScale(scale);
							}
						});
			}
			...
		}
		return sWindowSession; //private static IWindowSession sWindowSession,單例物件
	}
}

WindowManagerService.java

public IWindowSession openSession(IWindowSessionCallback callback) {
	return new Session(this, callback);
}

Session.java

public Session(WindowManagerService service, IWindowSessionCallback callback) {
	mService = service;
	mCallback = callback;
	mUid = Binder.getCallingUid();
	mPid = Binder.getCallingPid();
	...
	try {
		mCallback.asBinder().linkToDeath(this, 0);
	}
	...
}

(2)step 2:addToDisplay()

/frameworks/base/services/core/java/com/android/server/wm/Session.java

public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs, int viewVisibility, 
		int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, 
		DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState) {
    //mService 為 WMS
	return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame, outContentInsets, 
			outStableInsets, outOutsets, outDisplayCutout, outInputChannel, outInsetsState);
}

(3)step 3:addWindow()

/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public int addWindow(Session session, IWindow client, int seq, LayoutParams attrs, int viewVisibility, 
		int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, 
		DisplayCutout.ParcelableWrapper, InputChannel outInputChannel, InsetsState outInsetsState) {
	int[] appOp = new int[1];
	int res = mPolicy.checkAddPermission(attrs, appOp);
	if (res != WindowManagerGlobal.ADD_OKAY) {
		return res;
	}
	...
	WindowState parentWindow = null;
	long origId;
	final int callingUid = Binder.getCallingUid();
	final int type = attrs.type;

	synchronized (mGlobalLock) {
		//獲取DisplayContent(不存在就建立:mRoot.getDisplayContent(displayId))
		final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
		...

		if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
			parentWindow = windowForClientLocked(null, attrs.token, false);
			...
		}
		...
		AppWindowToken atoken = null;
		final boolean hasParent = parentWindow != null;
		//獲取 WindowToken(有父視窗,則獲取父視窗的)
		WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
		//獲取視窗 type(有父視窗,則獲取父視窗的)
		final int rootType = hasParent ? parentWindow.mAttrs.type : type;

		boolean addToastWindowRequiresToken = false;

		if (token == null) {
			...
			final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
			final boolean isRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
			token = new WindowToken(this, binder, type, false, displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
		} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
			atoken = token.asAppWindowToken();
			...
		}
		...
		else if (type == TYPE_TOAST) {
			addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName, callingUid, parentWindow);
			...
		}
		...
		else if (token.asAppWindowToken() != null) {
			attrs.token = null;
			token = new WindowToken(this, client.asBinder(), type, false, displayContent, session.mCanAddInternalSystemWindow);
		}
		//建立 WindowState,說明每個 Decor(根) View,都與一個 WindowState 一一對應
		final WindowState win = new WindowState(this, session, client, token, parentWindow, appOp[0], 
				seq, attrs, viewVisibility, session.mUid, session.mCanAddInternalSystemWindow);
		...
		final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
		displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(), Binder.getCallingUid());
		win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
		//前面已排除一些異常可能,接下來的程式碼不會有異常情況
		res = displayPolicy.prepareAddWindowLw(win, attrs);
		...

		final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
		if  (openInputChannels) {
			win.openInputChannel(outInputChannel);
		}
		if (type == TYPE_TOAST) {
			...
			if (addToastWindowRequiresToken || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
					|| displayContent.mCurrentFocus == null || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
				mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win), win.mAttrs.hideTimeoutMilliseconds);
			}
		}
		res = WindowManagerGlobal.ADD_OKAY;
		if (displayContent.mCurrentFocus == null) {
			displayContent.mWinAddedSinceNullFocus.add(win);
		}

		if (excludeWindowTypeFromTapOutTask(type)) {
			displayContent.mTapExcludedWindows.add(win);
		}

		origId = Binder.clearCallingIdentity();

		win.attach();
		mWindowMap.put(client.asBinder(), win);

		win.initAppOpsState();

		final boolean suspended = mPmInternal.isPackageSuspended(win.getOwningPackage(),
				UserHandle.getUserId(win.getOwningUid()));
		win.setHiddenWhileSuspended(suspended);

		final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
		win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);

		final AppWindowToken aToken = token.asAppWindowToken();
		if (type == TYPE_APPLICATION_STARTING && aToken != null) {
			aToken.startingWindow = win;
		}

		boolean imMayMove = true;

		win.mToken.addWindow(win);
		if (type == TYPE_INPUT_METHOD) {
			displayContent.setInputMethodWindowLocked(win);
			imMayMove = false;
		} else if (type == TYPE_INPUT_METHOD_DIALOG) {
			displayContent.computeImeTarget(true);
			imMayMove = false;
		} else {
			if (type == TYPE_WALLPAPER) {
				displayContent.mWallpaperController.clearLastWallpaperTimeoutTime();
				displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
			} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
				displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
			} else if (displayContent.mWallpaperController.isBelowWallpaperTarget(win)) {
				displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
			}
		}
		
		win.applyAdjustForImeIfNeeded();

		if (type == TYPE_DOCK_DIVIDER) {
			mRoot.getDisplayContent(displayId).getDockedDividerController().setWindow(win);
		}

		final WindowStateAnimator winAnimator = win.mWinAnimator;
		winAnimator.mEnterAnimationPending = true;
		winAnimator.mEnteringAnimation = true;
		// Check if we need to prepare a transition for replacing window first.
		if (atoken != null && atoken.isVisible() && !prepareWindowReplacementTransition(atoken)) {
			prepareNoneTransitionForRelaunching(atoken);
		}

		final DisplayFrames displayFrames = displayContent.mDisplayFrames;
		// TODO: Not sure if onDisplayInfoUpdated() call is needed.
		final DisplayInfo displayInfo = displayContent.getDisplayInfo();
		displayFrames.onDisplayInfoUpdated(displayInfo,
				displayContent.calculateDisplayCutoutForRotation(displayInfo.rotation));
		final Rect taskBounds;
		final boolean floatingStack;
		if (atoken != null && atoken.getTask() != null) {
			taskBounds = mTmpRect;
			atoken.getTask().getBounds(mTmpRect);
			floatingStack = atoken.getTask().isFloating();
		} else {
			taskBounds = null;
			floatingStack = false;
		}
		if (displayPolicy.getLayoutHintLw(win.mAttrs, taskBounds, displayFrames, floatingStack,
				outFrame, outContentInsets, outStableInsets, outOutsets, outDisplayCutout)) {
			res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
		}
		outInsetsState.set(displayContent.getInsetsStateController().getInsetsForDispatch(win));

		if (mInTouchMode) {
			res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
		}
		if (win.mAppToken == null || !win.mAppToken.isClientHidden()) {
			res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
		}

		displayContent.getInputMonitor().setUpdateInputWindowsNeededLw();

		boolean focusChanged = false;
		if (win.canReceiveKeys()) {
			focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
					false /*updateInputWindows*/);
			if (focusChanged) {
				imMayMove = false;
			}
		}

		if (imMayMove) {
			displayContent.computeImeTarget(true /* updateImeTarget */);
		}

		// Don't do layout here, the window must call
		// relayout to be displayed, so we'll do it there.
		win.getParent().assignChildLayers();

		if (focusChanged) {
			displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
					false /*updateInputWindows*/);
		}
		displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);

		if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "
				+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));

		if (win.isVisibleOrAdding() && displayContent.updateOrientationFromAppTokens()) {
			reportNewConfig = true;
		}
	}

	if (reportNewConfig) {
		sendNewConfiguration(displayId);
	}

	Binder.restoreCallingIdentity(origId);

	return res;
}

相關文章