一、强制设置方向
1.Activity
如果要强制设置一个Activity的横竖屏可以通过Manifest去设置,跟Activity相关的信息都会保存在ActivityInfo当中。
- android:screenOrientation=["unspecified" | "user" | "behind" |
- "landscape" | "portrait" |
- "reverseLandscape" | "reversePortrait" |
- "sensorLandscape" | "sensorPortrait" |
- "sensor" | "fullSensor" | "nosensor"]
2.Window
如果是要强制设置一个Window的横竖屏可以通过 LayoutParams.screenOrientation来设置。在通过WindowManager.addView的时候把对应的LayoutParams传递给WMS。
WindowManager.LayoutParams.screenOrientation
二、屏幕转屏
2.1 Activity -- 启动一个有设置screenOrientation的Activity
在ActivityStack.startActivityLocked中,AMS会把ActivityRecord相关的Token加到WMS中,有且仅在这个方法中。这时候会把screenOrientation传递过去。
atoken.requestedOrientation = requestedOrientation; 这个AppToken会在WMS获取相关App Orientation的时候其作用。
- mService.mWindowManager.addAppToken(addPos, r.userId, r.appToken,
- r.task.taskId, r.info.screenOrientation, r.fullscreen,
- (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
- public void addAppToken(int addPos, int userId, IApplicationToken token,
- int groupId, int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
- ... ...
- synchronized(mWindowMap) {
- AppWindowToken atoken = findAppWindowToken(token.asBinder());
- if (atoken != null) {
- Slog.w(TAG, "Attempted to add existing app token: " + token);
- return;
- }
- atoken = new AppWindowToken(this, userId, token);
- atoken.inputDispatchingTimeoutNanos = inputDispatchingTimeoutNanos;
- atoken.groupId = groupId;
- atoken.appFullscreen = fullscreen;
- atoken.showWhenLocked = showWhenLocked;
- atoken.requestedOrientation = requestedOrientation;//方向赋值
- if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
- + " at " + addPos);
- mAppTokens.add(addPos, atoken);
- addAppTokenToAnimating(addPos, atoken);
- mTokenMap.put(token.asBinder(), atoken);
- // Application tokens start out hidden.
- atoken.hidden = true;
- atoken.hiddenRequested = true;
- //dump();
- }
- }
2.2 在启动Activity之前去获取当前WMS中的Orientation的Config
在resumeTopActivityLocked和realStartActivityLocked中回去获取最新的Orientation的config
1. 通过mWindowManager.updateOrientationFromAppTokens去更新当前WMS中的Orientation值,把WMS中的config返回给AMS ,这个我们在之前http://blog.csdn.net/kc58236582/article/details/53741445博客分析过了
2. 通过updateConfigurationLocked去更新AMS中的config,发给每一个ActiviytThread。(会调用每个ActivityThread的onConfigurationChanged函数)
- synchronized (mService) {
- Configuration config = mService.mWindowManager.updateOrientationFromAppTokens(
- mService.mConfiguration,
- next.mayFreezeScreenLocked(next.app) ? next.appToken : null);
- if (config != null) {
- next.frozenBeforeDestroy = true;
- }
- updated = mService.updateConfigurationLocked(config, next, false, false);
- }
WMS.updateOrientationFroemAppTokens 会直接去调updateOrientationFromAppTokensLocked
1. 去调用updateOrientationFromAppTokensLocked(false) 做真正的update Orientation的工作,如果返回是true,说明方向发生了变化,就需要去做app的转屏动作。
2. 如果freezeThisOneIfNeed不为null,说明要做屏幕的转屏操做,就会把当前的atoken freeze掉。
3. computeNewConfigurationLocked() 计算当前的Configuration,然后返回给AMS。
- private Configuration updateOrientationFromAppTokensLocked(
- Configuration currentConfig, IBinder freezeThisOneIfNeeded) {
- Configuration config = null;
- if (updateOrientationFromAppTokensLocked(false)) {
- if (freezeThisOneIfNeeded != null) {
- AppWindowToken atoken = findAppWindowToken(
- freezeThisOneIfNeeded);
- if (atoken != null) {
- startAppFreezingScreenLocked(atoken,
- ActivityInfo.CONFIG_ORIENTATION);
- }
- }
- config = computeNewConfigurationLocked();
- } else if (currentConfig != null) {
- // No obvious action we need to take, but if our current
- // state mismatches the activity manager's, update it,
- // disregarding font scale, which should remain set to
- // the value of the previous configuration.
- mTempConfiguration.setToDefaults();
- mTempConfiguration.fontScale = currentConfig.fontScale;
- if (computeScreenConfigurationLocked(mTempConfiguration)) {
- if (currentConfig.diff(mTempConfiguration) != 0) {
- mWaitingForConfig = true;
- getDefaultDisplayContentLocked().layoutNeeded = true;
- startFreezingDisplayLocked(false, 0, 0);
- config = new Configuration(mTempConfiguration);
- }
- }
- }
- return config;
- }
updateOrientationFromAppTokensLocked 会查找出当前是否有需要强制Orientation的App或者Window
1. computeForcedAppOrientationLocked //goto 3.2.1.1.1
2. 如果ForcedAppOrientation发生了变化, 就会去通知WindowPolicy去设置当前的Sensor的状态。//goto 3.2.1.1.2
3. updateRotationUncheckedLocked(inTransaction),由于Orientation可能发生变化,所以需要去重新获取一下Rotation;具体就可以参照前一节了。
如果Rotation发生了变化就返回true。跟configuration相关的东西,大多都在这个函数中进行。
- boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
- long ident = Binder.clearCallingIdentity();
- try {
- int req = computeForcedAppOrientationLocked();
- if (req != mForcedAppOrientation) {
- mForcedAppOrientation = req;
- //send a message to Policy indicating orientation change to take
- //action like disabling/enabling sensors etc.,
- mPolicy.setCurrentOrientationLw(req);
- if (updateRotationUncheckedLocked(inTransaction)) {
- // changed
- return true;
- }
- }
- return false;
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
computeForcedAppOrientationLocked
这个函数的作用就是去查询当前即将要显示的Activity或者Window有没有需要强制Orientation的
1. 先通过 getOrientationFromWindowsLocked去遍历WMS中的WindowList。
1)如果在最上面的Window是一个AppWindow就直接返回mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
2)如果不是AppWindow,判断这个Window是否可见,如果不可见就continue
3)最后如果这个Window既不是AppWindow又要是可见的,并且他是有指定ScreenOrientation的,就返回这个window的orientatin。mLastWindowForcedOrientation=req
2. 如果getOrientationFromWindowsLocked找到的是一个AppWindow或者当前没有指定Orientation的Window,就会走到getOrientationFromAppTokensLocked();
1)这个函数会去遍历AppWindowToken List去查找需要强制Orientation的Token,这个时候就是根据我们之前在AMS中传递进来的atoken.requestedOrientation;来进行判断,如果设置了就返回回去给req。
- int computeForcedAppOrientationLocked() {
- int req = getOrientationFromWindowsLocked();
- if (req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
- req = getOrientationFromAppTokensLocked();
- }
- if (mForceLandScape &&
- req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
- req = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- }
- return req;
- }
PhoneWindowManager.setCurrentOrientationLw()
1. 用mCurrentAppOrientation保存当前新的Orientation.
2. updateOrientationListenerLp,通过名字就知道他只是去管理是否启动Sensor Listener的。
- public void setCurrentOrientationLw(int newOrientation) {
- synchronized (mLock) {
- if (newOrientation != mCurrentAppOrientation) {
- mCurrentAppOrientation = newOrientation;
- updateOrientationListenerLp();
- }
- }
- }
WMS. computeNewConfiguration()
通过computeNewConfigurationLocked() 计算出一个新的configuration.随后就会在AMS中调用ActivityThread把这个数据最后传到应用进程
- public Configuration computeNewConfiguration() {
- synchronized (mWindowMap) {
- Configuration config = computeNewConfigurationLocked();
- if (config == null && mWaitingForConfig) {
- // Nothing changed but we are waiting for something... stop that!
- mWaitingForConfig = false;
- performLayoutAndPlaceSurfacesLocked();
- }
- return config;
- }
- }
2.3 AMS端的工作完成之后,就到ActivityThread和ViewRootImpl的工作了
2.3.1 在ActivityThread的handleResume的时候会把Activity对应的DectorView加到WMS中,在WMS中会调用addWindow.
如果我们自己添加一个Window的话也是走到这边。 在AddWindow的时候有可能对Orietation和Configuration, 前提是这个win的isVisibleOrAdding的条件。不过一般都不会满足这个条件。
sendNewConfiguration最后会到AMS的updateConfiguration函数然后调用updateConfigurationLocked函数之前博客分析过了。
- if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
- reportNewConfig = true;
- }
- }
- if (reportNewConfig) {
- sendNewConfiguration();
- }
2.3.2 当addWindow完成之后,ViewRootImpl就会去requestLayout,而requestLayout实际上就是去做scheduleTraversals
1. 在performTraversals中会先去做一次measureHierarchy计算一下View所需的大小。
2. 然后会调用relayoutWindow去请求WMS帮助实际计算当前Window的布局,创建一个Surface,并且返回对应的Frame。
2.3.4 WMS.relayoutWindow()
1. 根据传进来的requestWidth和requestHeight 或者attrChange的变化来决定是不是真的要relayout,并且把win.mLayoutNeeded设置为true。在performLayoutAndPlaceSurfacesLocked的会根据这个值做判断。
2. 如果当前的WindowState还没有Surface就调用 winAnimator.createSurfaceLocked();去创建一个Surface会带回给ViewRootImpl
3. 如果focus window发生变化了就updateFocusedWindowLocked
如果Layer变化了就调用assignLayersLocked
4. 再次调用updateOrientationFromAppTokensLocked去判断当前的Orietation是不是发生变化了。//在这个调用中如果有Orientation发生了变化,如果有变化就会调用computeNewConfigurationLocked()去更新DisplayContent相关的信息并且计算出一个新的Configuration
5. 调用performLayoutAndPlaceSurfacesLocked
6. 把WindowState中的三个Frame返回给Client端。
- public int relayoutWindow(Session session, IWindow client, int seq,
- WindowManager.LayoutParams attrs, int requestedWidth,
- int requestedHeight, int viewVisibility, int flags,
- Rect outFrame, Rect outContentInsets,
- Rect outVisibleInsets, Configuration outConfig, Surface outSurface) {
- boolean toBeDisplayed = false;
- boolean inTouchMode;
- boolean configChanged;
- boolean surfaceChanged = false;
- boolean animating;
- ... ...
- synchronized(mWindowMap) {
- // TODO(cmautner): synchronize on mAnimator or win.mWinAnimator.
- WindowState win = windowForClientLocked(session, client, false);
- ... ...
- WindowStateAnimator winAnimator = win.mWinAnimator;
- if (win.mRequestedWidth != requestedWidth
- || win.mRequestedHeight != requestedHeight) {
- win.mLayoutNeeded = true;
- win.mRequestedWidth = requestedWidth;
- win.mRequestedHeight = requestedHeight;
- }
- if (attrs != null && seq == win.mSeq) {
- win.mSystemUiVisibility = systemUiVisibility;
- }
- if (attrs != null) {
- mPolicy.adjustWindowParamsLw(attrs);
- }
- winAnimator.mSurfaceDestroyDeferred =
- (flags&WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY) != 0;
- int attrChanges = 0;
- int flagChanges = 0;
- if (attrs != null) {
- flagChanges = win.mAttrs.flags ^= attrs.flags;
- attrChanges = win.mAttrs.copyFrom(attrs);
- if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
- | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
- win.mLayoutNeeded = true;
- }
- }
- ... ...
- if (viewVisibility == View.VISIBLE &&
- (win.mAppToken == null || !win.mAppToken.clientHidden)) {
- toBeDisplayed = !win.isVisibleLw();
- if ((attrChanges&WindowManager.LayoutParams.FORMAT_CHANGED) != 0) {
- // To change the format, we need to re-build the surface.
- winAnimator.destroySurfaceLocked(false);
- toBeDisplayed = true;
- surfaceChanged = true;
- }
- try {
- if (!win.mHasSurface) {
- surfaceChanged = true;
- }
- Surface surface = winAnimator.createSurfaceLocked();
- if (surface != null) {
- outSurface.copyFrom(surface);
- if (SHOW_TRANSACTIONS) Slog.i(TAG,
- " OUT SURFACE " + outSurface + ": copied");
- } else {
- // For some reason there isn't a surface. Clear the
- // caller's object so they see the same state.
- outSurface.release();
- }
- } catch (Exception e) {
- }
- if (toBeDisplayed) {
- focusMayChange = isDefaultDisplay;
- }
- if (win.mAttrs.type == TYPE_INPUT_METHOD
- && mInputMethodWindow == null) {
- mInputMethodWindow = win;
- imMayMove = true;
- }
- if (win.mAttrs.type == TYPE_BASE_APPLICATION
- && win.mAppToken != null
- && win.mAppToken.startingWindow != null) {
- // Special handling of starting window over the base
- // window of the app: propagate lock screen flags to it,
- // to provide the correct semantics while starting.
- final int mask =
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
- | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
- | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
- WindowManager.LayoutParams sa = win.mAppToken.startingWindow.mAttrs;
- sa.flags = (sa.flags&~mask) | (win.mAttrs.flags&mask);
- }
- } else {
- winAnimator.mEnterAnimationPending = false;
- if (winAnimator.mSurface != null) {
- if (DEBUG_VISIBILITY) Slog.i(TAG, "Relayout invis " + win
- + ": mExiting=" + win.mExiting);
- // If we are not currently running the exit animation, we
- // need to see about starting one.
- if (!win.mExiting) {
- surfaceChanged = true;
- // Try starting an animation; if there isn't one, we
- // can destroy the surface right away.
- int transit = WindowManagerPolicy.TRANSIT_EXIT;
- if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
- transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
- }
- if (win.isWinVisibleLw() &&
- winAnimator.applyAnimationLocked(transit, false)) {
- focusMayChange = isDefaultDisplay;
- win.mExiting = true;
- } else if (win.mWinAnimator.isAnimating()) {
- // Currently in a hide animation... turn this into
- // an exit.
- win.mExiting = true;
- } else if (win == mWallpaperTarget) {
- // If the wallpaper is currently behind this
- // window, we need to change both of them inside
- // of a transaction to avoid artifacts.
- win.mExiting = true;
- win.mWinAnimator.mAnimating = true;
- } else {
- if (mInputMethodWindow == win) {
- mInputMethodWindow = null;
- }
- winAnimator.destroySurfaceLocked(false);
- }
- scheduleNotifyWindowTranstionIfNeededLocked(win, transit);
- }
- }
- outSurface.release();
- if (DEBUG_VISIBILITY) Slog.i(TAG, "Releasing surface in: " + win);
- }
- if (focusMayChange) {
- //System.out.println("Focus may change: " + win.mAttrs.getTitle());
- if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
- false /*updateInputWindows*/)) {
- imMayMove = false;
- }
- //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
- }
- // updateFocusedWindowLocked() already assigned layers so we only need to
- // reassign them at this point if the IM window state gets shuffled
- boolean assignLayers = false;
- ... ...
- if (assignLayers) {
- assignLayersLocked(win.getWindowList());
- }
- configChanged = updateOrientationFromAppTokensLocked(false);
- performLayoutAndPlaceSurfacesLocked();
- if (toBeDisplayed && win.mIsWallpaper) {
- DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
- updateWallpaperOffsetLocked(win,
- displayInfo.appWidth, displayInfo.appHeight, false);
- }
- if (win.mAppToken != null) {
- win.mAppToken.updateReportedVisibilityLocked();
- }
- outFrame.set(win.mCompatFrame);
- outContentInsets.set(win.mContentInsets);
- outVisibleInsets.set(win.mVisibleInsets);
- ... ...
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- }
- if (configChanged) {
- sendNewConfiguration();
- }
WMS.performLayoutAndPlaceSurfacesLockedLoop()
performLayoutAndPlaceSurfacesLocked直接去调performLayoutAndPlaceSurfacesLockedLoop, 在performLayoutAndPlaceSurfacesLockedLoop里面做两个最主要的操作。
1. performLayoutAndPlaceSurfacesLockedInner();
2. requestTraversalLocked();
- private final void performLayoutAndPlaceSurfacesLockedLoop() {
- ... ...
- mInLayout = true;
- boolean recoveringMemory = false;
- ... ...
- try {
- performLayoutAndPlaceSurfacesLockedInner(recoveringMemory);
- mInLayout = false;
- if (needsLayout()) {
- if (++mLayoutRepeatCount < 6) {
- requestTraversalLocked();
- } else {
- Slog.e(TAG, "Performed 6 layouts in a row. Skipping");
- mLayoutRepeatCount = 0;
- }
- } else {
- mLayoutRepeatCount = 0;
- }
- if (mWindowsChanged && !mWindowChangeListeners.isEmpty()) {
- mH.removeMessages(H.REPORT_WINDOWS_CHANGE);
- mH.sendMessage(mH.obtainMessage(H.REPORT_WINDOWS_CHANGE));
- }
- } catch (RuntimeException e) {
- mInLayout = false;
- Log.wtf(TAG, "Unhandled exception while laying out windows", e);
- }
- Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
- }
WMS.performLayoutAndPlaceSurfacesLockedInner(false)
1. 首先会获取当前的一个DsiplayContent,由于这个DisplayContent在updateOrientationFromAppTokensLocked的时候已经根据最新的window状态进行跟新过,所以现在的DsiplayContent应该是最新的值。
2. performLayoutLockedInner; 这个函数回去遍历每一个Window,然后根据当前的DisplayContent去更新Layout.
这个方法会根据最新的DisplayContent去判断所有的window是否需要重新layout
第一步,通过mPolicy.beginLayoutLw去给policy设置一个标准把当前的Content信息设置进去
第二步, mPolicy.layoutWindowLw(win, win.mAttrs, null); 给win做真正的layout,layout主要做了什么?有几个重要的变量?
在layoutWindow中会通过computeFrameLw去计算WindowState的中的Frame,有三个Frame;
3. updateResizingWindows(w);
如果WindowState其中的w.mContentInsetsChanged || w.mVisibleInsetsChanged || winAnimator.mSurfaceResized|| configChanged 任意的一个发生变化了就会加入到mResizingWindows数组中。并且把对应的Window Freezing。
4. 遍历mResizingWindows, 调用win.mClient.resized通过IPC告诉Clinet端要做resize
最终ViewRootImpl就会发送一个MSG_RESIZED去处理,WMS会把里面的三个Frame和Configuration发回给Clinet。ViewRoot会保存最新的三个Frame和updateConfiguration,最后 requestLayout();
- private final void performLayoutAndPlaceSurfacesLockedInner(boolean recoveringMemory) {
- ... ...
- Surface.openTransaction();
- try {
- boolean focusDisplayed = false;
- boolean updateAllDrawn = false;
- DisplayContentsIterator iterator = new DisplayContentsIterator();
- while (iterator.hasNext()) {
- final DisplayContent displayContent = iterator.next();
- WindowList windows = displayContent.getWindowList();
- DisplayInfo displayInfo = displayContent.getDisplayInfo();
- final int displayId = displayContent.getDisplayId();
- final int dw = displayInfo.logicalWidth;
- final int dh = displayInfo.logicalHeight;
- final int innerDw = displayInfo.appWidth;
- final int innerDh = displayInfo.appHeight;
- final boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
- // Reset for each display unless we are forcing mirroring.
- if (mInnerFields.mDisplayHasContent != LayoutFields.DISPLAY_CONTENT_MIRROR) {
- mInnerFields.mDisplayHasContent = LayoutFields.DISPLAY_CONTENT_UNKNOWN;
- }
- ... ...
- int repeats = 0;
- do {
- repeats++;
- ... ...
- // FIRST LOOP: Perform a layout, if needed.
- if (repeats < 4) {
- performLayoutLockedInner(displayContent, repeats == 1,
- false /*updateInputWindows*/);
- } else {
- Slog.w(TAG, "Layout repeat skipped after too many iterations");
- }
- ... ...
- } while (displayContent.pendingLayoutChanges != 0);
- // Only used if default window
- final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
- final int N = windows.size();
- for (i=N-1; i>=0; i--) {
- WindowState w = windows.get(i);
- ... ...
- //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
- w.mContentChanged = false;
- // Moved from updateWindowsAndWallpaperLocked().
- if (w.mHasSurface) {
- // Take care of the window being ready to display.
- ... ...
- }
- ... ...
- }
- }
- if (isDefaultDisplay && someoneLosingFocus && (w == mCurrentFocus)
- && w.isDisplayedLw()) {
- focusDisplayed = true;
- }
- updateResizingWindows(w);
- }
- ... ...
- for (i = mResizingWindows.size() - 1; i >= 0; i--) {
- WindowState win = mResizingWindows.get(i);
- if (win.mAppFreezing) {
- // Don't remove this window until rotation has completed.
- continue;
- }
- final WindowStateAnimator winAnimator = win.mWinAnimator;
- try {
- if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG,
- "Reporting new frame to " + win + ": " + win.mCompatFrame);
- int diff = 0;
- boolean configChanged = win.isConfigChanged();
- if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION)
- && configChanged) {
- Slog.i(TAG, "Sending new config to window " + win + ": "
- + winAnimator.mSurfaceW + "x" + winAnimator.mSurfaceH
- + " / " + mCurConfiguration + " / 0x"
- + Integer.toHexString(diff));
- }
- win.setConfiguration(mCurConfiguration);
- if (DEBUG_ORIENTATION &&
- winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING) Slog.i(
- TAG, "Resizing " + win + " WITH DRAW PENDING");
- win.mClient.resized(win.mFrame, win.mLastContentInsets, win.mLastVisibleInsets,
- winAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING,
- configChanged ? win.mConfiguration : null);
- win.mContentInsetsChanged = false;
- win.mVisibleInsetsChanged = false;
- winAnimator.mSurfaceResized = false;
- } catch (RemoteException e) {
- win.mOrientationChanging = false;
- }
- mResizingWindows.remove(i);
- }
- ... ...
- // Finally update all input windows now that the window changes have stabilized.
- mInputMonitor.updateInputWindowsLw(true /*force*/);
- ... ...
- }
- // Check to see if we are now in a state where the screen should
- // be enabled, because the window obscured flags have changed.
- enableScreenIfNeededLocked();
- updateLayoutToAnimationLocked();
- ... ...
- }