framework——View新增過程
1 前言
WMS啟動流程 中介紹了 WindowManagerService 的啟動流程,本文將介紹 View 的新增流程,一共分為2步:
- 在應用程式程式中新增 View
- 在系統(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;
}
相關文章
- View 的繪製過程View
- Spring MVC framework 執行過程SpringMVCFramework
- Android View的繪製過程AndroidView
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- 在Entity Framework中使用儲存過程Framework儲存過程
- 初探Android的View繪製過程AndroidView
- android view draw原始碼過程分析AndroidView原始碼
- Android繪製View的過程研究——計算View的大小AndroidView
- 深入瞭解View的事件分發過程View事件
- android View measure過程原始碼解析AndroidView原始碼
- Qt 之 Graphics View Framework 簡介QTViewFramework
- nbu新增media server過程簡介Server
- 淺析Spring Framework框架容器啟動過程SpringFramework框架
- 【framework】framework中為systemserver新增許可權FrameworkServer
- 淺談移動端 View 的顯示過程View
- Android中父View和子view的點選事件的執行過程AndroidView事件
- View + django-rest-framework序列化ViewDjangoRESTFramework
- Entity Framework 6 Code First新特性:支援儲存過程Framework儲存過程
- Android原始碼完全解析——View的Measure過程Android原始碼View
- Android中View的測量和佈局過程AndroidView
- 探究Android View 繪製流程,Xml 檔案到 View 物件的轉換過程AndroidViewXML物件
- Git.Framework 框架隨手記--儲存過程簡化GitFramework框架儲存過程
- Oracle自動新增資料檔案過程Oracle
- 原始碼解析Android中View的layout佈局過程原始碼AndroidView
- 使用xcodeproj為工程新增frameworkXCodeFramework
- REST : rest_framework.decorators.api_view 實現PATCHRESTFrameworkAPIView
- Mysql使用儲存過程快速新增百萬資料MySql儲存過程
- job呼叫過程報錯 ORA-00942: table or view does not existView
- 原始碼解析Android中View的measure量算過程原始碼AndroidView
- [MySQL View]最有意思的檢視view優化過程,從30分鐘到0.08秒MySqlView優化
- Android自定義View 屬性新增AndroidView
- 給自定義View新增xml屬性ViewXML
- Entity Framework Code First執行SQL語句、檢視及儲存過程FrameworkSQL儲存過程
- .NET Framework (最新版本到.NET Framework 4.7.2)中的新增功能Framework
- Entity Framework Core 2.1,新增種子資料Framework
- ORACLE建庫過程中自動生成的跟鎖相關的VIEWOracleView
- 不使用Activity如何新增一個ViewView
- 為自定義的View新增長按事件View事件