Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> android Gui系統之WMS(2)----窗口的添加,androidwms

android Gui系統之WMS(2)----窗口的添加,androidwms

編輯:關於android開發

android Gui系統之WMS(2)----窗口的添加,androidwms


Android系統很多,但是最常用的就兩類,一類是有系統進場管理的,系統窗口。還有一類就是由應用程序產生的,應用窗口。

1.系統窗口的添加流程

1.1 addStatusBarWindow

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,然後設置一些屬性。

 

1.2 WMS addwindow

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; } addWindow

step1. 檢查權限

如果窗口類型是

        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值

1.3 addAppWindowToListLocked

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相關的。

 

2 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 內核設計思想》 林學森

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved