WindowManagerService原始碼學習
WindowManagerService 原始碼
Android 的framework 層主要是由 WindowManagerService 與 ActivityManagerService 以及 View 所構成,這三個模組穿插互動在 framework中。
WMS 和其他很多服務一樣, 都是由 SystemServer 啟動。
在SystemServer 中,有如下程式碼:
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY _TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
addService中 key 是 window。 那麼在其他程序就可以通過 ServiceManager 以key 為 window 來獲取到 WMS。
下面是WMS 中的 main 方法。 通過 Handler 的 runWithScissors 方法執行一個特殊的同步 Task. 並構建了 WMS 的例項。
public static WindowManagerService main(final Context context,
final InputManagerService im,
final boolean haveInputMethods, final boolean showBootMsgs,
final boolean onlyCore) {
final WindowManagerService[] holder = new WindowManagerService[1];
//執行同步 task
DisplayThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
//構建例項
holder[0] = new WindowManagerService(context, im,
haveInputMethods, showBootMsgs, onlyCore);
}
}, 0);
return holder[0];
}
Handler 的 runWithScissors
方法中 第一行註釋說明這是一個同步 task。如下:
- Runs the specified task synchronously.
WMS 的構造方法中主要是對一些視窗管理將要用到的成員變數 進行初始化:
private WindowManagerService(Context context, InputManagerService inputManager,
boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
mContext = context;
mHaveInputMethods = haveInputMethods;
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
com.android.internal.R.bool.config_sf_limitedAlpha);
mHasPermanentDpad = context.getResources().getBoolean(
com.android.internal.R.bool.config_hasPermanentDpad);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mDisplaySettings = new DisplaySettings(context);
// ... 省略幾行程式碼
//獲取顯示服務
mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
Display[] displays = mDisplayManager.getDisplays();
//為每一個 Display 分配一個 Content
for (Display display : displays) {
createDisplayContentLocked(display);
}
// ... 省略幾行程式碼
//構建App 事務
mAppTransition = new AppTransition(context, mH);
//獲取 IActivityManager 物件
mActivityManager = ActivityManagerNative.getDefault();
// ... 省略幾行程式碼
//構造 Window 動畫物件
mAnimator = new WindowAnimator(this);
// 初始化視窗策略
initPolicy();
//開啟繪製事務
SurfaceControl.openTransaction();
// ... 省略幾行程式碼
}
WMS 主要功能有兩個方面, 一是 對視窗的管理; 二是 對事件的管理和分發。
介面方法定義在 IWindowManager.aidl。 編譯後會生成 IWindowManager.java介面檔案。 裡面定義了 WMS 絕大部分的功能和方法。
WMS 定義了許多各種不同的視窗。
//已經完成啟動的應用
final ArrayList<AppWindowToken> mFinishedStarting = new ArrayList<AppWindowToken>();
//漸變視窗
final ArrayList<FakeWindowImpl> mFakeWindows = new ArrayList<FakeWindowImpl>();
//尺寸正在改變的視窗
final ArrayList<WindowState> mResizingWindows = new ArrayList<WindowState>();
//動畫正在結束的視窗
final ArrayList<WindowState> mPendingRemove = new ArrayList<WindowState>();
//即將釋放的Surface視窗
final ArrayList<WindowState> mDestroySurface = new ArrayList<WindowState>();
// 失去焦點的Window
ArrayList<WindowState> mLosingFocus = new ArrayList<WindowState>();
//為了釋放記憶體需要強制關閉的Window
ArrayList<WindowState> mForceRemoves;
//正在開啟的應用
final ArraySet<AppWindowToken> mOpeningApps = new ArraySet<AppWindowToken>();
//正在關閉的應用
final ArraySet<AppWindowToken> mClosingApps = new ArraySet<AppWindowToken>();
//得到焦點的應用
AppWindowToken mFocusedApp = null;
... //省略程式碼 還有很多。
不同的視窗或者同一個視窗在不同的階段可能會位於不同的表中。
Android 中主要由兩種視窗型別:
1.應用視窗。
如 Activity所處的視窗,對話方塊視窗、彈出的視窗等等。 與應用相關的 Window類的 PhoneWindow。 核心是DecorView, 通過 addView 將一個DecorView 新增到 WindowManager.
2.系統視窗。
如 螢幕頂部的狀態列 、 底部的導航欄、桌面的視窗等。 系統視窗沒有針對的封裝類, 只需要通過 WindwoManager的 addView方法 新增到WindowManager 即可。
比如PhoneStatusBar 中。 createAndAddWindows() -> StatusBarWindowManager.java -> add()中:
mWindowManager.addView(mStatusBarView, mLp);
客戶端新增一個視窗
WindowManager.addView() -> WindowManagerImpl.addView() -> WindowMangerGlobal.addView() -> ViewRootImpl.setView() -> IWindowSession.addToDisplay() -> Session.addToDisplay() -> WMS.addWindow().
Sesson中:
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel);
}
ok 。我們來看 WMS 中 addWindow 的實現:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
...// 省略一堆程式碼...
}
這裡的 mPolicy 實際上是 PhoneWindowManager. Policy 中的實現。
public WindowManagerPolicy makeNewWindowManager() {
return new PhoneWindowManager();
}
PhoneWindowManager 的 checkAddPermission 中會先判斷視窗的 type 是否是系統級別。 如果不是 則返回 WindowManagerGlobal.ADD_OKAY
. 如果是,就會進行相關許可權等的校驗 SYSTEM_ALERT_WINDOW
、INTERNAL_SYSTEM_WINDOW
. 如果沒有許可權就會 返回 ADD_PERMISSION_DENIED
. 校驗完畢許可權 會校驗視窗的有效性。
boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
if (token == null) {
//如果視窗是子視窗
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
//如果是輸入法視窗
if (type == TYPE_INPUT_METHOD) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
...// 省略一堆 if else.
檢查視窗的有效性完畢,接下來就是為該視窗建立一個 WindowState物件。維護視窗的狀態以及更加適當的機制來調整視窗狀態。
synchronized(mWindowMap) {
// ...省略程式碼
//構建WindowState物件
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
//如果客戶端已經被銷燬
if (win.mDeathRecipient == null) {
return WindowManagerGlobal.ADD_APP_EXITING;
}
//根據視窗型別調整屬性
mPolicy.adjustWindowParamsLw(win.mAttrs);
//設定該視窗是否只對自己的 uid 可見
win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));
//根據視窗型別判定許可權
res = mPolicy.prepareAddWindowLw(win, attrs);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
//如果輸出 Channel 的通道為空
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//建立通道
String name = win.makeInputChannelName();
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
win.setInputChannel(inputChannels[0]);
inputChannels[1].transferTo(outInputChannel);
//向 InputManager 中註冊該通道,以便當前視窗可以接受到事件。
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
//...省略一些程式碼
mPolicy 實質上就是 PhoneWindowManager。 對用它的 adjustWindowParamsLw
方法。裡面判斷如果是 TYPE_SYSTEM_OVERLAY 和 TYPE_SECURE_SYSTEM_OVERLAY 型別 的視窗,不要獲取焦點即可。
addWindow 最後部分的邏輯
... 省略大量程式碼
res = WindowManagerGlobal.ADD_OKAY;
//重置當前執行緒 IPC 的 ID
origId = Binder.clearCallingIdentity();
是否需要token
if (addToken) {
mTokenMap.put(attrs.token, token);
}
//將視窗新增至Session
win.attach();
//將視窗新增至 WMS
mWindowMap.put(client.asBinder(), win);
//省略幾行程式碼...
//順序排列視窗
if (type == TYPE_INPUT_METHOD) {
//如果是輸入法視窗
win.mGivenInsetsPending = true;
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
//如果是輸入法對話方塊視窗
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
imMayMove = false;
} else {
//如果是其他視窗
addWindowToListInOrderLocked(win, true);
if (type == TYPE_WALLPAPER) {
mLastWallpaperTimeoutTime = 0;
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (mWallpaperTarget != null
&& mWallpaperTarget.mLayer >= win.mBaseLayer) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
win.mWinAnimator.mEnterAnimationPending = true;
//如果displayContent 是預設的顯示
if (displayContent.isDefaultDisplay) {
//獲取系統視窗區域的inserts
mPolicy.getContentInsetHintLw(attrs, outContentInsets);
} else {
//否則將 outContentInsets 置為空
outContentInsets.setEmpty();
}
if (mInTouchMode) {
//標識使用者直接觸控的視窗
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
if (win.mAppToken == null || !win.mAppToken.clientHidden) {
//標識應用視窗
res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;
}
mInputMonitor.setUpdateInputWindowsNeededLw();
boolean focusChanged = false;
//如果當前視窗可以接收按鍵事件
if (win.canReceiveKeys()) {
//那麼更新焦點將視窗資訊存入 InputDispatcher
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
if (imMayMove) {
moveInputMethodWindowsIfNeededLocked(false);
}
//分配最終層級
assignLayersLocked(displayContent.getWindowList());
if (focusChanged) {
mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
}
mInputMonitor.updateInputWindowsLw(false /*force*/);
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;
}
}
if (reportNewConfig) {
sendNewConfiguration();
}
Binder.restoreCallingIdentity(origId);
return res;
}
這部分邏輯中,最重要的一步就是通過 WindowState的 attach方法 建立一個關聯的 SurfaceSession的物件 用以與 SurfaceFlinger通訊。 而 SurfaceSession實際對應的是 native層中的 SurfaceComposerClient物件。 SurfaceComposerClient 主要作用就是在 應用程序與 SurfaceFlinger 服務之間建立連線。(這塊不是很明白。很多知識都不理解)。
mSurfaceSession = new SurfaceSession();