案例
- 在下拉控制中心打开屏幕旋转开关。
- 用 Android Studio 创建一个 HelloWorld app,然后运行起来。
- 在 MainActivity 界面,把手机从竖屏旋转到横屏,会看到一个屏幕旋转动画。
获取 sensor上报的方向
WMS 通过监听 Sensor 得知设备方向改变
// WindowOrientationListener.java
final class OrientationSensorJudge extends OrientationJudge {
public void onSensorChanged(SensorEvent event) {
// 1.获取 sensor 上报的旋转方向
int reportedRotation = (int) event.values[0];
// ...
if (isRotationResolverEnabled()) {
// ...
} else {
finalizeRotation(reportedRotation);
}
}
private void finalizeRotation(int reportedRotation) {
int newRotation;
synchronized (mLock) {
mDesiredRotation = reportedRotation;
// 2. 计算旋转方向,一般来说就是 sensor 上报的方向
newRotation = evaluateRotationChangeLocked();
}
if (newRotation >= 0) {
mLastRotationResolution = newRotation;
mLastRotationResolutionTimeStamp = SystemClock.uptimeMillis();
// 3. 处理旋转方向改变,由宿主类 DisplayRotation 实现
onProposedRotationChanged(newRotation);
}
}
}
// DisplayRotation.java
private class OrientationListener extends WindowOrientationListener implements Runnable {
public void onProposedRotationChanged(@Surface.Rotation int rotation) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
// ..
if (isRotationChoiceAllowed(rotation)) {
// ...
} else {
mRotationChoiceShownToUserForConfirmation = ROTATION_UNDEFINED;
// 4. WMS 更新旋转
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
}
}
WMS 更新旋转
// WindowManagerService.java
// 两个参数,都是false
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "updateRotationUnchecked:"
+ " alwaysSendConfiguration=%b forceRelayout=%b",
alwaysSendConfiguration, forceRelayout);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation");
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
boolean layoutNeeded = false;
final int displayCount = mRoot.mChildren.size();
for (int i = 0; i < displayCount; ++i) {
final DisplayContent displayContent = mRoot.mChildren.get(i);
// DisplayContent 更新旋转
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
final boolean rotationChanged = displayContent.updateRotationUnchecked();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
// ...
}
// ...
}
} finally {
Binder.restoreCallingIdentity(origId);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
// DisplayContent.java
boolean updateRotationUnchecked() {
return mDisplayRotation.updateRotationUnchecked(false /* forceUpdate */);
}
// DisplayRotation.java
// forceUpdate 为 false
boolean updateRotationUnchecked(boolean forceUpdate) {
// ...
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
// 1. 计算旋转方向
int rotation = rotationForOrientation(lastOrientation, oldRotation);
// ...
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Display id=%d rotation changed to %d from %d, lastOrientation=%d",
displayId, rotation, oldRotation, lastOrientation);
// 保存计算出的新的旋转方向
mRotation = rotation;
// 标记 DisplayContent 需要 layout
mDisplayContent.setLayoutNeeded();
// 标记 DisplayContent 正在等待配置更新
mDisplayContent.mWaitingForConfig = true;
if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
// ...
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
oldRotation, mRotation);
// 2. 请求 Transition
mDisplayContent.requestChangeTransitionIfNeeded(
ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change);
// ...
return true;
}
// ...
}
计算旋转方向
MainActivity 没有声明/请求 orientation,orientation 默认为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED,sensor 上报的方向是 90°,最终计算出的旋转方向为 Surface.ROTATION_90,如下
// DisplayRotation.java
// orientation 为 ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
int rotationForOrientation(@ScreenOrientation int orientation,
@Surface.Rotation int lastRotation) {
// ...
// 获取sensor上报的方向
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
// ...
final int preferredRotation;
if (/* QTI_BEGIN */ !(overrideMirroring && isBuiltin) && /* QTI_END */ !isDefaultDisplay) {
} // ...
else if (((mUserRotationMode == WindowManagerPolicy.USER_ROTATION_FREE//旋转开关打开
|| isTabletopAutoRotateOverrideEnabled())
&& (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
|| orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER))
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
|| orientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) {
// sensorRotation 是 Surface.ROTATION_90
if (sensorRotation != Surface.ROTATION_180
|| getAllowAllRotations() == ALLOW_ALL_ROTATIONS_ENABLED
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|| orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
// 使用 sensor 方向作为优先的旋转方向
preferredRotation = sensorRotation;
} else {
}
}// ...
switch (orientation) {
// ...
default:
if (preferredRotation >= 0) {
return preferredRotation;
}
// ...
}
}
请求 Transition
// DisplayContent.java
// 参数 changes 值为 ActivityInfo.CONFIG_WINDOW_CONFIGURATION
// 参数 displayChange 保存旋转前后的方向
void requestChangeTransitionIfNeeded(@ActivityInfo.Config int changes,
@Nullable TransitionRequestInfo.DisplayChange displayChange) {
// ...
// 1. 请求类型为 CHANGE 的 Transition
final Transition t = controller.requestTransitionIfNeeded(TRANSIT_CHANGE, 0 /* flags */,
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
if (mFixedRotationLaunchingApp != null) {
// ...
} else if (isRotationChanging()) {
// ...
// 启动异步旋转,不在本文分析范围内
startAsyncRotation(false /* shouldDebounce */);
}
// 2. DisplayContent 的 ChangeInfo#mKnownConfigChanges
// 更新为 ActivityInfo.CONFIG_WINDOW_CONFIGURATION
t.setKnownConfigChanges(this, changes);
}
}
// TransitionController.java
// type 为 TRANSIT_CHANGE
// flags 为 0
// trigger 为 DisplayContent
// readyGroupRef 为 DisplayContent
// remoteTransition 为 null
// displayChange 保存了旋转前后的方向
Transition requestTransitionIfNeeded(@WindowManager.TransitionType int type,
@WindowManager.TransitionFlags int flags, @Nullable WindowContainer trigger,
@NonNull WindowContainer readyGroupRef, @Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange) {
// ...
Transition newTransition = null;
if (isCollecting()) {
} else {
// 1. 创建 Transition,并向 WM-Shell 发起请求
newTransition = requestStartTransition(createTransition(type, flags),
trigger != null ? trigger.asTask() : null, remoteTransition, displayChange);
// ...
}
// trigger 为 DisplayContent
if (trigger != null) {
if (isExistenceType(type)) {
// ...
} else {
// 2.Transition 收集 DisplayContent
collect(trigger);
}
}
return newTransition;
}
Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
@Nullable RemoteTransition remoteTransition,
@Nullable TransitionRequestInfo.DisplayChange displayChange) {
// ...
try {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
"Requesting StartTransition: %s", transition);
ActivityManager.RunningTaskInfo info = null;
// startTask 为 null
if (startTask != null) {
// ...
}
// 创建 TransitionRequestInfo,保存 Transition 信息
// transition.mType 值为 TRANSIT_CHANGE
// info 是 task 信息,此时为 null
// remoteTransition 为 null
// displayChange 仅仅保存了旋转前后的方向
final TransitionRequestInfo request = new TransitionRequestInfo(
transition.mType, info, remoteTransition, displayChange);
transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
transition.mLogger.mRequest = request;
// 向 wm-shell 发起 transition 请求
mTransitionPlayer.requestStartTransition(transition.getToken(), request);
if (remoteTransition != null) {
}
} catch (RemoteException e) {
}
return transition;
}
Wm-Shell 处理 Transition 请求
Wm-Shell 收到请求后,主要是建立自己 Transition 数据,然后通知 Wm-Core 执行 start transition。
// Transitions.java
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
transitionToken, request);
// ...
final ActiveTransition active = new ActiveTransition();
// ...
// 1. 通知 WM-Core start transition
mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
// 2. mPendingTransitions 保存 Transition 数据
active.mToken = transitionToken;
mPendingTransitions.add(0, active);
}
WM-Core start transition
// WindowOrganizerController.java
// t 虽然不为 null,但是数据为空
public void startTransition(@NonNull IBinder transitionToken,
@Nullable WindowContainerTransaction t) {
startTransition(-1 /* unused type */, transitionToken, t);
}
private IBinder startTransition(@WindowManager.TransitionType int type,
@Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {
enforceTaskPermission("startTransition()");
final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
// 根据 token 获取 Transition
Transition transition = Transition.fromBinder(transitionToken);
// ...
// wct 此时为 null
final WindowContainerTransaction wct =
t != null ? t : new WindowContainerTransaction();
// ...
// 1. start transition
transition.start();
transition.mLogger.mStartWCT = wct;
// 2. apply WCT
applyTransaction(wct, -1 /*syncId*/, transition, caller);
return transition.getToken();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) {
return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
}
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) {
int effects = TRANSACT_EFFECTS_NONE;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
// 推迟窗口刷新
mService.deferWindowLayout();
// 推迟 Activity 可见性更新
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
// apply display change
if (transition != null) {
transition.applyDisplayChangeIfNeeded();
}
// ...
} finally {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
return effects;
}
// Transition.java
void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
// Transition 有收集过 DisplayContent
final WindowContainer<?> wc = mParticipants.valueAt(i);
final DisplayContent dc = wc.asDisplayContent();
if (dc == null || !mChanges.get(dc).hasChanged()) continue;
// 1. DisplayContent更新配置
dc.sendNewConfiguration();
if (!mReadyTracker.mUsed) {
// 2. set ready
setReady(dc, true);
}
}
}
DisplayContent 更新配置
// DisplayContent.java
void sendNewConfiguration() {
// ...
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
if (configUpdated) {
return;
}
// ...
}
boolean updateDisplayOverrideConfigurationLocked() {
// ...
// 1. 计算新配置
Configuration values = new Configuration();
computeScreenConfiguration(values);
// ...
// 2. 更新 display override config
updateDisplayOverrideConfigurationLocked(values, null /* starting */,
false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
// true
return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
}
首先,主要根据新的旋转方向,计算新配置
// DisplayContent.java
void computeScreenConfiguration(Configuration config) {
// 根据旋转方向,更新 DisplayContent#mDisplayInfo,
// 同时也会填充数据到 config
final DisplayInfo displayInfo = updateDisplayAndOrientation(config);
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
mTmpRect.set(0, 0, dw, dh);
config.windowConfiguration.setBounds(mTmpRect);
config.windowConfiguration.setMaxBounds(mTmpRect);
// ...
computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation);
// ...
}
private DisplayInfo updateDisplayAndOrientation(Configuration outConfig) {
// 获取 DisplayRotation 计算的旋转方向
final int rotation = getRotation();
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;
final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;
// ...
// 更新 mDisplayInfo
mDisplayInfo.rotation = rotation;
mDisplayInfo.logicalWidth = dw;
mDisplayInfo.logicalHeight = dh;
// ...
// 把 mDisplayInfo 更新到 DMS, DMS 会通知 WMS 发起窗口刷新,窗口刷新会收集 DMS 的 transcation 到 DisplayContent sync transcation,从而在 ST 中 apply。
mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,
mDisplayInfo);
// ...
// 更新 DisplayFrames、system gesture 等
onDisplayInfoChanged();
return mDisplayInfo;
}
private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,
int rotation) {
final DisplayPolicy.DecorInsets.Info info =
mDisplayPolicy.getDecorInsetsInfo(rotation, dw, dh);
// AppBounds at the root level should mirror the app screen size.
outConfig.windowConfiguration.setAppBounds(info.mNonDecorFrame);
outConfig.windowConfiguration.setRotation(rotation);
outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
// ...
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, dw, dh);
outConfig.windowConfiguration.setDisplayRotation(rotation);
}
然后,根据新配置,更新 DisplayContent 的 override config
// DisplayContent.java
// values 为计算出的新配置
// starting 为 null
// deferResume 为 false
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
ActivityRecord starting, boolean deferResume,
ActivityTaskManagerService.UpdateConfigurationResult result) {
// ...
mAtmService.deferWindowLayout();
try {
if (values != null) {
if (mDisplayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config, so
// we're calling global config update instead for default display. It will also
// apply the correct override config.
// 1. 先从 ATMS 的 global config 开始更新
changes = mAtmService.updateGlobalConfigurationLocked(values,
false /* initLocale */, false /* persistent */,
UserHandle.USER_NULL /* userId */);
} else {
// ...
}
}
if (!deferResume) {
// 2. 确保 top activity 能处理配置改变
// 由于 apply WCT 时,推迟了可见性更新,因此这里不会执行可见性更新
kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
}
} finally {
mAtmService.continueWindowLayout();
}
// ...
}
要更新 DisplayContent 的配置,首先 ATMS 计算 global config
// ActivityTaskManagerService.java
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId) {
// 1. 计算新的 global config
mTempConfig.setTo(getGlobalConfiguration());//global config 就是 RWC full config
final int changes = mTempConfig.updateFrom(values);
// ...
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateGlobalConfiguration");
ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
+ "to: %s", values);
// ...
mTempConfig.seq = increaseConfigurationSeqLocked();
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
// ...
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
// 2. 把新的 global config 发给 RWC
mRootWindowContainer.onConfigurationChanged(mTempConfig);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changes;
}
得到新的 global config 后,发送给 RWC,让其先更新配置
RWC#onConfigurationChanged() 由基类 ConfigurationContainer 提供实现。这是一个标准的配置更新函数,RWC 主要是把它的 full config 发送给 DisplayContent
// ConfigurationContainer.java
public void onConfigurationChanged(Configuration newParentConfig) {
// ...
mFullConfiguration.setTo(newParentConfig);
mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
// ...
// 把 full config 发送给 child,即 DisplayContent
for (int i = getChildCount() - 1; i >= 0; --i) {
// RWC 有实现这个函数
dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
}
}
// RootWindowContainer.java
void dispatchConfigurationToChild(DisplayContent child, Configuration config) {
if (child.isDefaultDisplay) {
// The global configuration is also the override configuration of default display.
// 先更新 DisplayContent override config
child.performDisplayOverrideConfigUpdate(config);
} else {
// ...
}
}
RWC 把它的 full config 发送给 DisplayContent,让其先更新 override config
// DisplayContent.java
// values 是 RWC 的 full config
int performDisplayOverrideConfigUpdate(Configuration values) {
// 1. 计算新的 override config
mTempConfig.setTo(getRequestedOverrideConfiguration());
final int changes = mTempConfig.updateFrom(values);
if (changes != 0) {
Slog.i(TAG, "Override config changes=" + Integer.toHexString(changes) + " "
+ mTempConfig + " for displayId=" + mDisplayId);
// ...
// 2. 更新 override config
onRequestedOverrideConfigurationChanged(mTempConfig);
// ...
}
return changes;
}
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
// 1. 旋转方向改变了,应用新的旋转方向
if (currRotation != ROTATION_UNDEFINED && overrideRotation != ROTATION_UNDEFINED
&& currRotation != overrideRotation) {
applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
}
mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
// 2. 通过基类函数,更新 requested override config
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
mCurrentOverrideConfigurationChanges = 0;
// 配置已经更新完成,重置 mWaitingForConfig
if (mWaitingForConfig) {
mWaitingForConfig = false;
mWmService.mLastFinishedFreezeSource = "new-config";
}
// 添加 layout reason
// aply WCT 结束时的 continue layout 会发起窗口刷新
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
所谓的应用新方向,是为了把屏幕的方向等数据,发到底层 SurfaceFlinger,但是用的是 DisplayContent sync transaction 的 setDisplayProjection 方式
// DisplayContent.java
private void applyRotationAndFinishFixedRotation(int oldRotation, int newRotation) {
// null
final WindowToken rotatedLaunchingApp = mFixedRotationLaunchingApp;
if (rotatedLaunchingApp == null) {
applyRotation(oldRotation, newRotation);
return;
}
// ...
}
private void applyRotation(final int oldRotation, final int rotation) {
// ...
// DisplayContent 的 sync transcation
final Transaction transaction =
shellTransitions ? getSyncTransaction() : getPendingTransaction();
// 前面在计算新的 global config 时,调用过这个函数
// 它主要是根据新旋转方向,更新 DisplayContent#mDisplayInfo,并且把它更到到 DMS
updateDisplayAndOrientation(null /* outConfig */);
// ...
// 立即通知 DMS 重新配置屏幕数据,其中一个数据就是屏幕旋转方向
// 这些数据会以 transaction 的 setDisplayProjection 进行保存
// 当 start transcation apply 时,屏幕的新旋转方向就会发送给 SurfaceFlinger
mWmService.mDisplayManagerInternal.performTraversal(transaction);
if (shellTransitions) {
// Before setDisplayProjection is applied by the start transaction of transition,
// set the transform hint to avoid using surface in old rotation.
getPendingTransaction().setFixedTransformHint(mSurfaceControl, rotation);
// The sync transaction should already contains setDisplayProjection, so unset the
// hint to restore the natural state when the transaction is applied.
transaction.unsetFixedTransformHint(mSurfaceControl);
}
// ...
}
继续看 DisplayContent override config 更新
// WindowContainer.java
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// We must diff before the configuration is applied so that we can capture the change
// against the existing bounds.
// 旋转方向的改变,会造成 bounds change
final int diff = diffRequestedOverrideBounds(
overrideConfiguration.windowConfiguration.getBounds());
// 1. 通过基类 ConfigurationContainer 函数,继续更新 override config
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
if (mParent != null) {
// DisplayContent 有实现此函数,它会发起窗口刷新
mParent.onDescendantOverrideConfigurationChanged();
}
// ...
if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
// DisplayContent 下的所有窗口需要 resize
// 最终结果是把 DisplayContent 下的所有 WindowState 保存到 resizing list 中
onResize();
} else {
// ...
}
}
// ConfigurationContainer.java
// overrideConfiguration 是 RWC 的 full config
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// 1. mRequestedOverrideConfiguration 保存参数 override config
updateRequestedOverrideConfiguration(overrideConfiguration);
// Update full configuration of this container and all its children.
final ConfigurationContainer parent = getParent();
// 2. 更新 DisplayContent 配置
// 注意,参数是 RWC 的 full config
// DisplayContent 有实现这个函数
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
}
void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) {
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
// mRequestedOverrideConfiguration 保存 requested override config
mRequestedOverrideConfiguration.setTo(overrideConfiguration);
final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
if (mHasOverrideConfiguration && providesMaxBounds()
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
}
}
// DisplayContent.java
public void onConfigurationChanged(Configuration newParentConfig) {
final int lastOrientation = getConfiguration().orientation;
// 通过基类 DisplayArea 处理配置更新
super.onConfigurationChanged(newParentConfig);
// ...
}
// DisplayArea.java
public void onConfigurationChanged(Configuration newParentConfig) {
// 收集 DisplayArea(实际为 DisplayContent) 可见性改变
mTransitionController.collectForDisplayAreaChange(this);
mTmpConfiguration.setTo(getConfiguration());
// 通过基类 WindowContainer 的函数,更新配置
// 最终还是通过基类 ConfigurationContainer 标准的配置更新函数,完成 DisplayContent 配置更新
super.onConfigurationChanged(newParentConfig);
// ...
}
DisplayContent 最终还是通过基类 ConfigurationContainer 标准的配置更新函数完成配置更新,最终更新所有 children 的配置,这里就不再深究流程。接下来主要看如何收集 DisplayArea(实际为 DisplayContent) 可见性改变
// TransitionController.java
// wc 此时是 DisplayContent
void collectForDisplayAreaChange(@NonNull DisplayArea<?> wc) {
// Transition 有收集过 DisplayContent
final Transition transition = mCollectingTransition;
if (transition == null || !transition.mParticipants.contains(wc)) return;
// 1. 为 DisplayContent 创建截图层
transition.collectVisibleChange(wc);
// 2. Transition 收集 DisplayContent 下所有可见的 task
// Collect all visible tasks.
wc.forAllLeafTasks(task -> {
if (task.isVisible()) {
transition.collect(task);
}
}, true /* traverseTopToBottom */);
// 3. Transition 收集DisplayContent下所有非 app 窗口
// Collect all visible non-app windows which need to be drawn before the animation starts.
final DisplayContent dc = wc.asDisplayContent();
if (dc != null) {
final boolean noAsyncRotation = dc.getAsyncRotationController() == null;
wc.forAllWindows(w -> {
if (w.mActivityRecord == null && w.isVisible() && !isCollecting(w.mToken)
&& (noAsyncRotation || !AsyncRotationController.canBeAsync(w.mToken))) {
transition.collect(w.mToken);
}
}, true /* traverseTopToBottom */);
}
}
Transition 收集 WC 就略过,这里主要看如何为 DisplayContent 创建一个截图层,并显示在最上层
// Transition.java
/**
* Records that a particular container is changing visibly (ie. something about it is changing
* while it remains visible). This only effects windows that are already in the collecting
* transition.
*/
// 参数 wc 是 DisplayContent
void collectVisibleChange(WindowContainer wc) {
// ...
if (mContainerFreezer == null) {
mContainerFreezer = new ScreenshotFreezer();
}
// ...
// 冻结 wc,其实就是为 wc 创建并显示一个截图层
mContainerFreezer.freeze(wc, change.mAbsoluteBounds);
}
// Transition.java
private class ScreenshotFreezer implements IContainerFreezer {
private final ArraySet<WindowContainer> mFrozen = new ArraySet<>();
/** Takes a screenshot and puts it at the top of the container's surface. */
@Override
public boolean freeze(@NonNull WindowContainer wc, @NonNull Rect bounds) {
// ...
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Screenshotting %s [%s]",
wc.toString(), bounds.toString());
// 参数 bounds 是 DisplayContent full config Bound
Rect cropBounds = new Rect(bounds);
cropBounds.offsetTo(0, 0);
// true
final boolean isDisplayRotation = wc.asDisplayContent() != null
&& wc.asDisplayContent().isRotationChanging();
// 1. 创建截图层的 buffer
ScreenCapture.LayerCaptureArgs captureArgs =
// wc 是 DisplayContent
new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl())
.setSourceCrop(cropBounds)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.setHintForSeamlessTransition(isDisplayRotation)
.build();
ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
ScreenCapture.captureLayers(captureArgs);
final HardwareBuffer buffer = screenshotBuffer == null ? null
: screenshotBuffer.getHardwareBuffer();
// ...
// 2. 创建截图层 surface
// 截图层的名字叫 RotationLayer
final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc;
SurfaceControl snapshotSurface = wc.makeAnimationLeash()
.setName(name)
.setOpaque(wc.fillsParent())
.setParent(wc.getSurfaceControl())
.setSecure(screenshotBuffer.containsSecureLayers())
.setCallsite("Transition.ScreenshotSync")
.setBLASTLayer()
.build();
// 保存冻结的 WC,即 DisplayContent
mFrozen.add(wc);
// 3. DisplayContent 的 ChangeInfo 中保存截图 surface
final ChangeInfo changeInfo = Objects.requireNonNull(mChanges.get(wc));
changeInfo.mSnapshot = snapshotSurface;
// ...
// 4. 在最上层显示截图层 surface
SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();
TransitionAnimation.configureScreenshotLayer(t, snapshotSurface, screenshotBuffer);
t.show(snapshotSurface);
t.setLayer(snapshotSurface, Integer.MAX_VALUE);
t.apply();
t.close();
buffer.close();
// TODO: 这个操作对于屏幕旋转动画来说,似乎是多余的,但是对其他动画可能有用?
// Detach the screenshot on the sync transaction (the screenshot is just meant to
// freeze the window until the sync transaction is applied (with all its other
// corresponding changes), so this is how we unfreeze it.
wc.getSyncTransaction().reparent(snapshotSurface, null /* newParent */);
return true;
}
}
Activity 处理配置改变
配置更新完成后,会让 top activity 处理配置改变
// DisplayContent.java
// values 为计算出的新配置
// starting 为 null
// deferResume 为 false
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
ActivityRecord starting, boolean deferResume,
ActivityTaskManagerService.UpdateConfigurationResult result) {
int changes = 0;
boolean kept = true;
mAtmService.deferWindowLayout();
try {
if (values != null) {
if (mDisplayId == DEFAULT_DISPLAY) {
// Override configuration of the default display duplicates global config, so
// we're calling global config update instead for default display. It will also
// apply the correct override config.
// 1.更新默认屏幕配置
changes = mAtmService.updateGlobalConfigurationLocked(values,
false /* initLocale */, false /* persistent */,
UserHandle.USER_NULL /* userId */);
} else {
// ...
}
}
if (!deferResume) {
// 2. top activity 处理配置改变
// 由于 apply WCT 推迟了可见性更新,因此这里不会执行可见性更新
kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
}
} finally {
mAtmService.continueWindowLayout();
}
// ...
return kept;
}
// ActivityTaskManagerService.java
// starting 为 null
// changes 为 CONFIG_WINDOW_CONFIGURATION|CONFIG_SCREEN_SIZE|CONFIG_ORIENTATION
boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
final Task mainRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
if (mainRootTask != null) {
if (changes != 0 && starting == null) {
// 获取 top focused root task 下的 top non-finishing activity
starting = mainRootTask.topRunningActivity();
}
if (starting != null) {
// top activity 处理配置改变
kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */);
// 这里不会更新 activity 可见性,因为 apply WCT 时,推迟了可见性更新
mRootWindowContainer.ensureActivitiesVisible(starting, changes,
!PRESERVE_WINDOWS);
}
}
return kept;
}
// ActivityRecord.java
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow) {
return ensureActivityConfiguration(globalChanges, preserveWindow,
false /* ignoreVisibility */, false /* isRequestedOrientationChanged */);
}
// globalChanges 值为 CONFIG_WINDOW_CONFIGURATION | CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
// 其他都为 false
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreVisibility, boolean isRequestedOrientationChanged) {
final Task rootTask = getRootTask();
// ...
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Ensuring correct "
+ "configuration: %s", this);
// ...
// 保存上次发送给 app 的 merged override config
mTmpConfig.setTo(mLastReportedConfiguration.getMergedConfiguration());
// ...
// Okay we now are going to make this activity have the new config.
// But then we need to figure out how it needs to deal with that.
// Find changes between last reported merged configuration and the current one. This is used
// to decide whether to relaunch an activity or just report a configuration change.
// 1. 检测 ActivityRecord full config 与 last reported merged configuration 差异
// changes 代表需要 Actvity 处理的配置改变
// 通过下面的 log 可知 changes 值为 CONFIG_ORIENTATION
final int changes = getConfigurationChanges(mTmpConfig);
final Configuration newMergedOverrideConfig = getMergedOverrideConfiguration();
// 更新 mLastReportedConfiguration 的 mGlobalConfig、mOverrideConfig
setLastReportedConfiguration(getProcessGlobalConfiguration(), newMergedOverrideConfig);
// ...
// 这里 log 记录了需要 Activity 处理那些配置改变
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Configuration changes for %s, "
+ "allChanges=%s", this, Configuration.configurationDiffToString(changes));
// ...
// 这个log也很有用,它对比当前配置改变与Actvity能处理的配置改变,这就决定了 Activity 是否要 relaunch
// info.getRealConfigChanged() 代表 Activity 声明的能处理的配置改变
// Figure out how to handle the changes between the configurations.
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Checking to restart %s: changed=0x%s, "
+ "handles=0x%s, mLastReportedConfiguration=%s", info.name,
Integer.toHexString(changes), Integer.toHexString(info.getRealConfigChanged()),
mLastReportedConfiguration);
// 2. 检测 activity 是否需要 relaunch
// 原来很简单, 如果 Activity 声明的能处理的配置改变,不包含当前需要处理的配置改变,那么就要 relaunch
boolean shouldRelaunchLocked = shouldRelaunchLocked(changes, mTmpConfig);
// ...
if (shouldRelaunchLocked || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
// ...
// preserveWindow 初始为 false,那么这里还是为 false
// Do not preserve window if it is freezing screen because the original window won't be
// able to update drawn state that causes freeze timeout.
preserveWindow &= isResizeOnlyChange(changes) && !mFreezingScreen;
// ...
if (mState == PAUSING) {
} else {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Config is relaunching %s",
this);
if (!mVisibleRequested) {
}
// 3. relaunch activity
relaunchActivityLocked(preserveWindow);
}
// All done... tell the caller we weren't able to keep this activity around.
return false;
}
// Default case: the activity can handle this new configuration, so hand it over.
// NOTE: We only forward the override configuration as the system level configuration
// changes is always sent to all processes when they happen so it can just use whatever
// system level configuration it last got.
if (displayChanged) {
} else {
// Activity 能处理配置改变,就直接通知Activity配置更新,即调用 Activity#onConfigurationChanged()
// 本文分析的案例,不走这里
scheduleConfigurationChanged(newMergedOverrideConfig);
}
// ...
}
本文使用的案例的 MainActivity 并没有在 AndroidManifest 中声明可以处理哪些配置改变,因此要执行 Activity relaunch 流程,即通知 app 端 relaunch activity
// ActivityRecord.java
// preserveWindow 为 false
void relaunchActivityLocked(boolean preserveWindow) {
// ...
// true
final boolean andResume = shouldBeResumed(null /*activeActivity*/);
// ...
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
"Relaunching: " + this + " with results=" + pendingResults
+ " newIntents=" + pendingNewIntents + " andResume=" + andResume
+ " preserveWindow=" + preserveWindow);
if (andResume) {
// event log 表明 activity 发生 relaunch
EventLogTags.writeWmRelaunchResumeActivity(mUserId, System.identityHashCode(this),
task.mTaskId, shortComponentName, Integer.toHexString(configChangeFlags));
} else {
// ...
}
// ...
try {
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
forceNewConfig = false;
// ActivityRecord#mPendingRelaunchCount + 1, 并且清除 ActivityRecord all drawn 状态
startRelaunching();
// 通知 app 端 relaunch activity
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) { // 走这里
// reluanch 后需要 resume,那么生命周期执行到 onResumed()
lifecycleItem = ResumeActivityItem.obtain(isTransitionForward(),
shouldSendCompatFakeFocus());
} else {
// 从这里可以看出,relaunch activity 并不一定要把生命后期执行到 onResume()
lifecycleItem = PauseActivityItem.obtain();
}
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), token);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
} catch (RemoteException e) {}
// ...
}
配置更新时序图
sequenceDiagram
Transition ->> DisplayContent : sendNewConfiguration
DisplayContent ->> DisplayContent : computeScreenConfiguration 计算新配置
DisplayContent ->> ActivityTaskManagerService : updateGlobalConfigurationLocked
ActivityTaskManagerService ->>ActivityTaskManagerService: 使用 RWC full config 计算新的 global config
ActivityTaskManagerService ->> RootWindowContainer : onConfigurationChanged 发送新 global config 给 RWC
RootWindowContainer ->> DisplayContent : performDisplayOverrideConfigUpdate
activate DisplayContent
DisplayContent ->> DisplayContent: onRequestedOverrideConfigurationChanged
activate DisplayContent
DisplayContent ->> DisplayContent:applyRotationAndFinishFixedRotation
DisplayContent ->> WindowContainer : onRequestedOverrideConfigurationChanged
deactivate DisplayContent
deactivate DisplayContent
WindowContainer ->> ConfigurationContainer : onRequestedOverrideConfigurationChanged
activate ConfigurationContainer
ConfigurationContainer ->> ConfigurationContainer : updateRequestedOverrideConfiguration
ConfigurationContainer ->> RootWindowContainer : onConfigurationChanged
deactivate ConfigurationContainer
RootWindowContainer ->> DisplayArea : onConfigurationChanged
activate DisplayArea
DisplayArea ->> TransitionController : collectForDisplayAreaChange
note over DisplayArea, TransitionController : 创建截图层、收集可见 Task
DisplayArea ->> WindowContainer : onConfigurationChanged
deactivate DisplayArea
WindowContainer ->> ConfigurationContainer : onConfigurationChanged
note right of ConfigurationContainer:DisplayContent把配置发给所有children
DisplayContent ->> ActivityTaskManagerService : ensureConfigAndVisibilityAfterUpdate