編輯:關於android開發
Android系統很多,但是最常用的就兩類,一類是有系統進場管理的,系統窗口。還有一類就是由應用程序產生的,應用窗口。
PhoneStatus.java中
private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowManager = new StatusBarWindowManager(mContext); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); }
getStatusBarHeight() 獲取狀態欄的高度
public int getStatusBarHeight() { if (mNaturalBarHeight < 0) { final Resources res = mContext.getResources(); mNaturalBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } return mNaturalBarHeight; }
可以看到,高度是固定的,在dimen裡面配置。
看看add方法
public void add(View statusBarView, int barHeight) { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, barHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); }
layoutparams屬性,還有設置在頂部。
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
軟鍵盤被覆蓋,絕大多數情況下,statusbar的顯示不會影響到軟件盤的位置,如果有,軟鍵盤調整,statusbar優先顯示。
最後是將statusbar加入到WMS裡面。
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
所以mWindowManager就是WMS的一個bind代理。
然後在看makeStatusBarView:
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; Resources res = context.getResources(); updateDisplaySize(); // populates mDisplayMetrics updateResources(); mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); mStatusBarWindow.setService(this); mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); if (event.getAction() == MotionEvent.ACTION_DOWN) { if (mExpandedVisible) { animateCollapsePanels(); } } return mStatusBarWindow.onTouchEvent(event); } }); mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar); mStatusBarView.setBar(this); PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder); mStatusBarView.setPanelHolder(holder); mNotificationPanel = (NotificationPanelView) mStatusBarWindow.findViewById( R.id.notification_panel); mNotificationPanel.setStatusBar(this); if (!ActivityManager.isHighEndGfx()) { mStatusBarWindow.setBackground(null); mNotificationPanel.setBackground(new FastColorDrawable(context.getColor( R.color.notification_panel_solid_background))); } mHeadsUpManager = new HeadsUpManager(context, mStatusBarWindow); mHeadsUpManager.setBar(this); mHeadsUpManager.addListener(this); mHeadsUpManager.addListener(mNotificationPanel); mNotificationPanel.setHeadsUpManager(mHeadsUpManager); mNotificationData.setHeadsUpManager(mHeadsUpManager); if (MULTIUSER_DEBUG) { mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById( R.id.header_debug_info); mNotificationPanelDebugText.setVisibility(View.VISIBLE); } try { boolean showNav = mWindowManagerService.hasNavigationBar(); if (DEBUG) Log.v(TAG, "hasNavigationBar=" + showNav); if (showNav) { mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout.navigation_bar, null); mNavigationBarView.setDisabledFlags(mDisabled1); mNavigationBarView.setBar(this); mNavigationBarView.setOnVerticalChangedListener( new NavigationBarView.OnVerticalChangedListener() { @Override public void onVerticalChanged(boolean isVertical) { if (mAssistManager != null) { mAssistManager.onConfigurationChanged(); } mNotificationPanel.setQsScrimEnabled(!isVertical); } }); mNavigationBarView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { checkUserAutohide(v, event); return false; }}); } } catch (RemoteException ex) { // no window manager? good luck with that } mAssistManager = new AssistManager(this, context); // figure out which pixel-format to use for the status bar. mPixelFormat = PixelFormat.OPAQUE; mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById( R.id.notification_stack_scroller); mStackScroller.setLongPressListener(getNotificationLongClicker()); mStackScroller.setPhoneStatusBar(this); mStackScroller.setGroupManager(mGroupManager); mStackScroller.setHeadsUpManager(mHeadsUpManager); mGroupManager.setOnGroupChangeListener(mStackScroller); mKeyguardIconOverflowContainer = (NotificationOverflowContainer) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false); mKeyguardIconOverflowContainer.setOnActivatedListener(this); mKeyguardIconOverflowContainer.setOnClickListener(mOverflowClickListener); mStackScroller.setOverflowContainer(mKeyguardIconOverflowContainer); SpeedBumpView speedBump = (SpeedBumpView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_speed_bump, mStackScroller, false); mStackScroller.setSpeedBumpView(speedBump); mEmptyShadeView = (EmptyShadeView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_no_notifications, mStackScroller, false); mStackScroller.setEmptyShadeView(mEmptyShadeView); mDismissView = (DismissView) LayoutInflater.from(mContext).inflate( R.layout.status_bar_notification_dismiss_all, mStackScroller, false); mDismissView.setOnButtonClickListener(new View.OnClickListener() { @Override public void onClick(View v) { MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES); clearAllNotifications(); } }); mStackScroller.setDismissView(mDismissView); mExpandedContents = mStackScroller; mBackdrop = (BackDropView) mStatusBarWindow.findViewById(R.id.backdrop); mBackdropFront = (ImageView) mBackdrop.findViewById(R.id.backdrop_front); mBackdropBack = (ImageView) mBackdrop.findViewById(R.id.backdrop_back); ScrimView scrimBehind = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_behind); ScrimView scrimInFront = (ScrimView) mStatusBarWindow.findViewById(R.id.scrim_in_front); View headsUpScrim = mStatusBarWindow.findViewById(R.id.heads_up_scrim); mScrimController = new ScrimController(scrimBehind, scrimInFront, headsUpScrim, mScrimSrcModeEnabled); mHeadsUpManager.addListener(mScrimController); mStackScroller.setScrimController(mScrimController); mScrimController.setBackDropView(mBackdrop); mStatusBarView.setScrimController(mScrimController); mDozeScrimController = new DozeScrimController(mScrimController, context); mHeader = (StatusBarHeaderView) mStatusBarWindow.findViewById(R.id.header); mHeader.setActivityStarter(this); mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view); mKeyguardBottomArea = (KeyguardBottomAreaView) mStatusBarWindow.findViewById(R.id.keyguard_bottom_area); mKeyguardBottomArea.setActivityStarter(this); mKeyguardBottomArea.setAssistManager(mAssistManager); mKeyguardIndicationController = new KeyguardIndicationController(mContext, (KeyguardIndicationTextView) mStatusBarWindow.findViewById( R.id.keyguard_indication_text), mKeyguardBottomArea.getLockIcon()); mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); // set the inital view visibility setAreThereNotifications(); mIconController = new StatusBarIconController( mContext, mStatusBarView, mKeyguardStatusBar, this); // Background thread for any controllers that need it. mHandlerThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); mHandlerThread.start(); // Other icons mLocationController = new LocationControllerImpl(mContext, mHandlerThread.getLooper()); // will post a notification mBatteryController = new BatteryController(mContext); mBatteryController.addStateChangedCallback(new BatteryStateChangeCallback() { @Override public void onPowerSaveChanged() { mHandler.post(mCheckBarModes); if (mDozeServiceHost != null) { mDozeServiceHost.firePowerSaveChanged(mBatteryController.isPowerSave()); } } @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { // noop } }); mNetworkController = new NetworkControllerImpl(mContext, mHandlerThread.getLooper()); mHotspotController = new HotspotControllerImpl(mContext); mBluetoothController = new BluetoothControllerImpl(mContext, mHandlerThread.getLooper()); mSecurityController = new SecurityControllerImpl(mContext); if (mContext.getResources().getBoolean(R.bool.config_showRotationLock)) { mRotationLockController = new RotationLockControllerImpl(mContext); } mUserInfoController = new UserInfoController(mContext); mVolumeComponent = getComponent(VolumeComponent.class); if (mVolumeComponent != null) { mZenModeController = mVolumeComponent.getZenController(); } mCastController = new CastControllerImpl(mContext); final SignalClusterView signalCluster = (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterKeyguard = (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterQs = (SignalClusterView) mHeader.findViewById(R.id.signal_cluster); mNetworkController.addSignalCallback(signalCluster); mNetworkController.addSignalCallback(signalClusterKeyguard); mNetworkController.addSignalCallback(signalClusterQs); signalCluster.setSecurityController(mSecurityController); signalCluster.setNetworkController(mNetworkController); signalClusterKeyguard.setSecurityController(mSecurityController); signalClusterKeyguard.setNetworkController(mNetworkController); signalClusterQs.setSecurityController(mSecurityController); signalClusterQs.setNetworkController(mNetworkController); final boolean isAPhone = mNetworkController.hasVoiceCallingFeature(); if (isAPhone) { mNetworkController.addEmergencyListener(mHeader); } mFlashlightController = new FlashlightController(mContext); mKeyguardBottomArea.setFlashlightController(mFlashlightController); mKeyguardBottomArea.setPhoneStatusBar(this); mKeyguardBottomArea.setUserSetupComplete(mUserSetup); mAccessibilityController = new AccessibilityController(mContext); mKeyguardBottomArea.setAccessibilityController(mAccessibilityController); mNextAlarmController = new NextAlarmController(mContext); mKeyguardMonitor = new KeyguardMonitor(mContext); if (UserSwitcherController.isUserSwitcherAvailable(UserManager.get(mContext))) { mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor, mHandler); } mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext, (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mKeyguardStatusBar, mNotificationPanel, mUserSwitcherController); // Set up the quick settings tile panel mQSPanel = (QSPanel) mStatusBarWindow.findViewById(R.id.quick_settings_panel); if (mQSPanel != null) { final QSTileHost qsh = new QSTileHost(mContext, this, mBluetoothController, mLocationController, mRotationLockController, mNetworkController, mZenModeController, mHotspotController, mCastController, mFlashlightController, mUserSwitcherController, mKeyguardMonitor, mSecurityController); mQSPanel.setHost(qsh); mQSPanel.setTiles(qsh.getTiles()); mBrightnessMirrorController = new BrightnessMirrorController(mStatusBarWindow); mQSPanel.setBrightnessMirror(mBrightnessMirrorController); mHeader.setQSPanel(mQSPanel); qsh.setCallback(new QSTileHost.Callback() { @Override public void onTilesChanged() { mQSPanel.setTiles(qsh.getTiles()); } }); } // User info. Trigger first load. mHeader.setUserInfoController(mUserInfoController); mKeyguardStatusBar.setUserInfoController(mUserInfoController); mKeyguardStatusBar.setUserSwitcherController(mUserSwitcherController); mUserInfoController.reloadUserInfo(); mHeader.setBatteryController(mBatteryController); ((BatteryMeterView) mStatusBarView.findViewById(R.id.battery)).setBatteryController( mBatteryController); mKeyguardStatusBar.setBatteryController(mBatteryController); mHeader.setNextAlarmController(mNextAlarmController); PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mBroadcastReceiver.onReceive(mContext, new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF)); mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "GestureWakeLock"); mVibrator = mContext.getSystemService(Vibrator.class); // receive broadcasts IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); filter.addAction(Intent.ACTION_SCREEN_OFF); filter.addAction(Intent.ACTION_SCREEN_ON); context.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null); IntentFilter demoFilter = new IntentFilter(); if (DEBUG_MEDIA_FAKE_ARTWORK) { demoFilter.addAction(ACTION_FAKE_ARTWORK); } demoFilter.addAction(ACTION_DEMO); context.registerReceiverAsUser(mDemoReceiver, UserHandle.ALL, demoFilter, android.Manifest.permission.DUMP, null); // listen for USER_SETUP_COMPLETE setting (per-user) resetUserSetupObserver(); // disable profiling bars, since they overlap and clutter the output on app windows ThreadedRenderer.overrideProperty("disableProfileBars", "true"); // Private API call to make the shadows look better for Recents ThreadedRenderer.overrideProperty("ambientRatio", String.valueOf(1.5f)); return mStatusBarView; } makeStatusBarView看著代碼很多,很復雜。其實總的來說很簡單,就是new了一個view,然後設置一些屬性。
WMS並不關心View的Tree的具體內容,它只要知道個應用進程的顯示界面大小,“層級值”(這些信息包含在windowmanager.layoutparams)
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) { int[] appOp = new int[1]; int res = mPolicy.checkAddPermission(attrs, appOp); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } boolean reportNewConfig = false; WindowState attachedWindow = null; long origId; final int type = attrs.type; synchronized(mWindowMap) { if (!mDisplayReady) { throw new IllegalStateException("Display has not been initialialized"); } final DisplayContent displayContent = getDisplayContentLocked(displayId); if (displayContent == null) { Slog.w(TAG, "Attempted to add window to a display that does not exist: " + displayId + ". Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (!displayContent.hasAccess(session.mUid)) { Slog.w(TAG, "Attempted to add window to a display for which the application " + "does not have access: " + displayId + ". Aborting."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; } if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } } if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) { Slog.w(TAG, "Attempted to add private presentation window to a non-private display. Aborting."); return WindowManagerGlobal.ADD_PERMISSION_DENIED; } boolean addToken = false; WindowToken token = mTokenMap.get(attrs.token); if (token == null) { if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { Slog.w(TAG, "Attempted to add application window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_VOICE_INTERACTION) { Slog.w(TAG, "Attempted to add voice interaction window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } if (type == TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG, "Attempted to add Accessibility overlay window with unknown token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } token = new WindowToken(this, attrs.token, -1, false); addToken = true; } else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) { AppWindowToken atoken = token.appWindowToken; if (atoken == null) { Slog.w(TAG, "Attempted to add window with non-application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_NOT_APP_TOKEN; } else if (atoken.removed) { Slog.w(TAG, "Attempted to add window with exiting application token " + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) { // No need for this guy! if (localLOGV) Slog.v( TAG, "**** NO NEED TO START: " + attrs.getTitle()); return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; } } else if (type == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { Slog.w(TAG, "Attempted to add input method window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_VOICE_INTERACTION) { if (token.windowType != TYPE_VOICE_INTERACTION) { Slog.w(TAG, "Attempted to add voice interaction window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_WALLPAPER) { if (token.windowType != TYPE_WALLPAPER) { Slog.w(TAG, "Attempted to add wallpaper window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_DREAM) { if (token.windowType != TYPE_DREAM) { Slog.w(TAG, "Attempted to add Dream window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (type == TYPE_ACCESSIBILITY_OVERLAY) { if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) { Slog.w(TAG, "Attempted to add Accessibility overlay window with bad token " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_APP_TOKEN; } } else if (token.appWindowToken != null) { Slog.w(TAG, "Non-null appWindowToken for system window of type=" + type); // It is not valid to use an app token with other system types; we will // instead make a new token for it (as if null had been passed in for the token). attrs.token = null; token = new WindowToken(this, null, -1, false); addToken = true; } WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent); if (win.mDeathRecipient == null) { // Client has apparently died, so there is no reason to // continue. Slog.w(TAG, "Adding window client " + client.asBinder() + " that is dead, aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } if (win.getDisplayContent() == null) { Slog.w(TAG, "Adding window to Display that has been removed."); return WindowManagerGlobal.ADD_INVALID_DISPLAY; } mPolicy.adjustWindowParamsLw(win.mAttrs); win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs)); res = mPolicy.prepareAddWindowLw(win, attrs); if (res != WindowManagerGlobal.ADD_OKAY) { return res; } 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); mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); } // From now on, no exceptions or errors allowed! res = WindowManagerGlobal.ADD_OKAY; origId = Binder.clearCallingIdentity(); if (addToken) { mTokenMap.put(attrs.token, token); } win.attach(); mWindowMap.put(client.asBinder(), win); if (win.mAppOp != AppOpsManager.OP_NONE) { int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(), win.getOwningPackage()); if ((startOpResult != AppOpsManager.MODE_ALLOWED) && (startOpResult != AppOpsManager.MODE_DEFAULT)) { win.setAppOpVisibilityLw(false); } } if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) { token.appWindowToken.startingWindow = win; if (DEBUG_STARTING_WINDOW) Slog.v (TAG, "addWindow: " + token.appWindowToken + " startingWindow=" + win); } boolean imMayMove = true; 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) { // If there is currently a wallpaper being shown, and // the base layer of the new window is below the current // layer of the target window, then adjust the wallpaper. // This is to avoid a new window being placed between the // wallpaper and its target. displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; } } final WindowStateAnimator winAnimator = win.mWinAnimator; winAnimator.mEnterAnimationPending = true; winAnimator.mEnteringAnimation = true; if (displayContent.isDefaultDisplay) { mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets, outOutsets); } else { outContentInsets.setEmpty(); outStableInsets.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()) { focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS, false /*updateInputWindows*/); if (focusChanged) { imMayMove = false; } } if (imMayMove) { moveInputMethodWindowsIfNeededLocked(false); } assignLayersLocked(displayContent.getWindowList()); // Don't do layout here, the window must call // relayout to be displayed, so we'll do it there. if (focusChanged) { mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/); } mInputMonitor.updateInputWindowsLw(false /*force*/); if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG, "addWindow: New client " + client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5)); if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) { reportNewConfig = true; } } if (reportNewConfig) { sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); return res; } addWindowstep1. 檢查權限
如果窗口類型是
if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) { // Window manager will make sure these are okay. return WindowManagerGlobal.ADD_OKAY; }
非系統窗口,任何app 都可以添加,就沒有什麼問題。
step2.避免重復添加
if (mWindowMap.containsKey(client.asBinder())) { Slog.w(TAG, "Window " + client + " is already added"); return WindowManagerGlobal.ADD_DUPLICATE_ADD; }
step3.子窗口
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) { attachedWindow = windowForClientLocked(null, attrs.token, false); if (attachedWindow == null) { Slog.w(TAG, "Attempted to add window with token that is not a window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW && attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) { Slog.w(TAG, "Attempted to add window with token that is a sub-window: " + attrs.token + ". Aborting."); return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN; } }
通過token,尋找client windowForClientLocked
Step4. token 檢查
根據type的類型,檢查token的有效性。
這裡有好幾個token相關的變量
WindowToken token = mTokenMap.get(attrs.token);
attrs.token
attrs.token 類型為IBinder。它代表這個窗口的“主人”.AMS 為每一個activity對象都創建一個ActivityRecord對象,本質就是IBinder。
boolean addToken = false;
用以表述新的token有沒有被添加
這是幾個token之間的關系圖。
1)如果attrs.token在mTokenMap中找不到的話,說明AMS沒有對應的記錄
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) TYPE_INPUT_METHOD TYPE_VOICE_INTERACTION TYPE_WALLPAPER TYPE_DREAM TYPE_ACCESSIBILITY_OVERLAY
這些是由AMS記錄的,不會找不到,如果沒有,說明出現不可預知的錯誤。返回
2)除了上述type之外,WMS允許AMS沒有記錄的情況,則為這個窗口創建一個token
attrs.token在mTokenMap可以找到的話,此時只有上面檢查的那些
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) TYPE_INPUT_METHOD TYPE_VOICE_INTERACTION TYPE_WALLPAPER TYPE_DREAM TYPE_ACCESSIBILITY_OVERLAY
才有可能被找到。所以,需要做一些判斷和比較。
Step5. WindowState
如果一切順利,new 一個WindowState
WindowState win = new WindowState(this, session, client, token, attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
this代表WMS自身
session 是WMS給窗口使用者的IWindowSession
client是IWindow,即窗口給WMS的一個訪問通道
step6
if (win.mDeathRecipient == null)
如果窗口已經dead,就不需要進行下去。
step6
mPolicy.adjustWindowParamsLw(win.mAttrs);
step7
通過Policy進行調整。
if (addToken) { mTokenMap.put(attrs.token, token); }
step8 添加新創建的token
if (addToken) { mTokenMap.put(attrs.token, token); }
step9 重新排列layer值。
因為我們新增加了一個窗口,很可能需要插入,所以需要進行排序。
step10. getInsetHintLw
if (displayContent.isDefaultDisplay) { mPolicy.getInsetHintLw(win.mAttrs, mRotation, outContentInsets, outStableInsets, outOutsets);
計算窗口的tContentInset
step11.給排好序列的窗口,最終賦一個layer值
private void addWindowToListInOrderLocked(final WindowState win, boolean addToToken) { if (DEBUG_FOCUS_LIGHT) Slog.d(TAG, "addWindowToListInOrderLocked: win=" + win + " Callers=" + Debug.getCallers(4)); if (win.mAttachedWindow == null) { final WindowToken token = win.mToken; int tokenWindowsPos = 0; if (token.appWindowToken != null) { tokenWindowsPos = addAppWindowToListLocked(win); } else { addFreeWindowToListLocked(win); } if (addToToken) { if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Adding " + win + " to " + token); token.windows.add(tokenWindowsPos, win); } } else { addAttachedWindowToListLocked(win, addToToken); } if (win.mAppToken != null && addToToken) { win.mAppToken.allAppWindows.add(win); } }
1)win.mAttachedWindow == null
最外圍的if,判斷的是,是不是子類窗口,我們這裡分析的是statusbar是系統窗口,所以考慮==null的情況
2)token.appWindowToken != null
當前窗口是不是activity相關的,因為appWindowToken 是activity相關的。
WMS添加窗口是不區分系統還是應用的窗口,只是他們的權限還有層級不一樣。
AMS在啟動Activity的時候,會把必要的信息傳遞給WMS
\services\core\java\com\android\server\am\ActivityStack.java
final void startActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition, Bundle options) { mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen, (r.info.flags & ActivityInfo.FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges, task.voiceSession != null, r.mLaunchTaskBehind); }
WMS 會把token記錄到我們之前看到的mTokenMap中
Activity什麼時候會經歷Add view 進而通過WMS addwindow 把窗口添加到系統中呢?
我們看下ActivityThread中的handleResumeActivity
ActivityClientRecord r = performResumeActivity(token, clearHide);
首先Resume的啟動,就是在這裡執行的
DecorView是每個Activity的最外面的一層布局。
if (r.window == null && !a.mFinished && willBeVisible) { r.window = r.activity.getWindow(); View decor = r.window.getDecorView();//DecorView decor.setVisibility(View.INVISIBLE);//可見性 ViewManager wm = a.getWindowManager();//WindowManagerImpl WindowManager.LayoutParams l = r.window.getAttributes();//窗口參數 a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;//窗口類型
l.softInputMode |= forwardBit; if (a.mVisibleFromClient) { a.mWindowAdded = true; wm.addView(decor, l);
}
將窗口添加wm.addView(decor, l)
在WMS添加窗口的過程基本與上面statusbar類似。
在WMS中:
private int addAppWindowToListLocked(final WindowState win)
這裡分2中情況
1)application已經含有窗口的情況下
if (!tokenWindowList.isEmpty()) { // If this application has existing windows, we // simply place the new window on top of them... but // keep the starting window on top.
2)如果沒有窗口,情況就復雜一點
WMS將部分token的數據放到了
AppTokenList tokens = tasks.get(taskNdx).mAppTokens;
在所有apptoken中,尋找當前token適合的位子。
// We now know the index into the apps. If we found // an app window above, that gives us the position; else // we need to look some more. if (pos != null) {
如果找到了合適的位置,pos就有值。
check WindowToken的位置,如果有對應的,就直接返回對應的pos
WindowState bottom = tokenWindowList.get(0); if (bottom.mSubLayer < 0) { pos = bottom; } }
如果參考窗口沒有,就直接選中最後一個。
如果pos == null,也就是沒有找到對應的參考值。
這時候就尋找下一個token對應的值。如果找到就用pos
如果還沒有找到,那只能對應BaseLayer的值,找到一個合適的位置。
參考:
《深入理解Android 內核設計思想》 林學森
android的消息機制 很多以前掌握的知識,總是慢慢的再忘記,看來還是自己理解的不夠透徹,希望用博客的形式記錄下來。 說起android的消息機制,那不得不提Handl
Drawable Animation 幀動畫,drawableanimationMySurfaceView類: package com.fm; import andr
【原】tinker dex文件格式的dump工具tinker-dex-dump,dextinker-dex-dump序言 Tinker是微信推出的熱更新開源項目,同其它熱
Android基於監聽的事件處理機制 Android提供了強大的事件處理機制,主要包括兩大類: 1,基於監聽的事件處理機制:主要做法是為Android界面組件綁定特定