frameworks 之屏幕旋转
frameworks 之 屏幕旋转
- 1 初始化和传感器监听
- 2 旋转的判断,开始冻屏以及动画准备
- 2.1 旋转的判断
- 2.2 准备进退动画
- 2.3 屏幕冻结
- 2.4 通知systemUi,并等待回调
- 3 更新动画以及DisplayInfo
- 3.1 更新DisplayInfo 和 Configuration信息
- 3.2 通知更新
- 3.2.1 Application的通知
- 3.2.2 容器的大小通知
- 3.3 Activity界面更新
- 3.3.1 重建Activity
- 3.3.2 非重建
- 4 动画的播放
- 4.1 停止冻屏
- 4.1 播放动画
- 5 堆栈
- 5.1 更新当前app屏幕配置
- 5.2 传感器上报
- 5.3开始冻屏幕
- 5.4 创建旋转动画
- 5.5 冻结触发时机
讲解 屏幕旋转的起点到冻屏以及解冻的时机
涉及到的类如下
- frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java
- frameworks/base/services/java/com/android/server/SystemServer.java
- frameworks/base/services/core/java/com/android/server/wm/WindowOrientationListener.java
- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
- frameworks/base/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
- frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java
- frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
- frameworks/base/services/core/java/com/android/server/wm/WindowProcessController.java
- frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
- frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
- frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java
- frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
- frameworks/base/core/jni/android_hardware_SensorManager.cpp
1 初始化和传感器监听
SystemServer 启动执行 startBootstrapServices 方法时候,就会启动对应的传感器监听
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// 启动传感器服务
t.traceBegin("StartSensorService");
mSystemServiceManager.startService(SensorService.class);
t.traceEnd();
}
DisplayContent 初始化的时候,会构造 DisplayRotation 类,该类主要负责监听和管控当前屏幕应用的旋转策略等。
DisplayContent(Display display, RootWindowContainer root) {
// 初始化屏幕旋转相关,包括监听等
mDisplayRotation = new DisplayRotation(mWmService, this);
}
而 DisplayRotation 的构造方法,
- 会判断是否默认屏幕,是的会创建对应的OrientationListener,该监听继承自windowOrientationListener
- 注册对应的SettingsObserver, 监听用户切换锁定屏幕的操作
DisplayRotation(...) {
...
// 如果是默认屏幕,创建对应的 OrientationListener,会有对应的监听
if (isDefaultDisplay) {
final Handler uiHandler = UiThread.getHandler();
// OrientationListener继承windowOrientationListener,里面会初始化,
// 有变化时候 SystemSensorManager 会触发dispatchSensorEvent 方法,
// 而该方法又会触发该父类WindowOrientationListener的onSensorChanged,在onSensorChanged会进行判断是否改变,
// 改变的话会调用子类即为OrientationListener的onProposedRotationChanged方法
mOrientationListener = new OrientationListener(mContext, uiHandler);
mOrientationListener.setCurrentRotation(mRotation);
mSettingsObserver = new SettingsObserver(uiHandler);
mSettingsObserver.observe();
}
}
DisplayRotation 有一些比较重要的方法
- updateOrientation 方法更新对应的应用程序方向策略。
该方法每次当应用进入resume状态的时候,则会一次调用该方法(具体调用可以看堆栈第一个)
boolean updateOrientation(@ScreenOrientation int newOrientation, boolean forceUpdate) {
// 更新当前APP的方向设置放到 mCurrentAppOrientation
if (newOrientation == mLastOrientation && !forceUpdate) {
return false;
}
mLastOrientation = newOrientation;
if (newOrientation != mCurrentAppOrientation) {
mCurrentAppOrientation = newOrientation;
if (isDefaultDisplay) {
// 有改变则注册或者移除对应的监听
updateOrientationListenerLw();
}
}
return updateRotationUnchecked(forceUpdate);
}
- updateOrientationListenerLw 方法会根据当前的配置和系统设置,调用mOrientationListener 的 enable 或者disable 去移除或者注册对应的监听。 具体逻辑在 WindowOrientationListener基类。
private void updateOrientationListenerLw() {
...
boolean disable = true;
...
if (screenOnEarly
&& (awake || mOrientationListener.shouldStayEnabledWhileDreaming())
&& ((keyguardDrawComplete && windowManagerDrawComplete))) {
if (needSensorRunning()) { // 判断是否需要运行
disable = false;
// Enable listener if not already enabled.
if (!mOrientationListener.mEnabled) {
...
mOrientationListener.enable(true /* clearCurrentRotation */);
}
}
}
// Check if sensors need to be disabled.
if (disable) {
mOrientationListener.disable();
}
}
- needSensorRunning 会根据当前用户设置,判断是否需要监听
private boolean needSensorRunning() {
...
// 就算设置为屏幕锁定,但是有mSupportAutoRotation,也会返回true可用
if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) {
...
return mSupportAutoRotation &&
mShowRotationSuggestions == Settings.Secure.SHOW_ROTATION_SUGGESTIONS_ENABLED;
}
return mSupportAutoRotation;
}
WindowOrientationListener 的构造方法,获取对应的SensorManager,实际为SystemSensorManager(继承这SensorManager), 并创建对应AccelSensorJudge,当调用enable的时候会将该监听通过 mSensorManager 注册给底层。
private WindowOrientationListener(
Context context, Handler handler, int rate) {
...
// 获取对应的感应器管理类
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
..
// enable的时候会调用 regiter注册该AccelSensorJudge监听
if (mOrientationJudge == null) {
mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist
mOrientationJudge = new AccelSensorJudge(context);
}
} }
注册
public void enable(boolean clearCurrentRotation) {
synchronized (mLock) {
...
if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {
mSensorManager.registerListener(
mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);
} else {
mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
}
mEnabled = true;
}
}
当底层有变化的时候,android_hardware_SensorManager.handleEvent 接收对应的事件会通过 调用java层SystemSensorManager的 dispatchSensorEvent 方法。
virtual int handleEvent(int fd, int events, void* data) {
JNIEnv* env = AndroidRuntime::getJNIEnv();
ASensorEvent buffer[16];
while ((n = q->read(buffer, 16)) > 0) {
for (int i=0 ; i<n ; i++) {
}else {
...
if (receiverObj.get()) {
// 通知到java层
env->CallVoidMethod(receiverObj.get(),
gBaseEventQueueClassInfo.dispatchSensorEvent,
buffer[i].sensor,
mFloatScratch,
status,
buffer[i].timestamp);
}
}
}
mSensorQueue->sendAck(buffer, n);
}
}
而 SystemSensorManager 的 dispatchSensorEvent 方法,会调用之前注册进入的listener(AccelSensorJudge) 的 onSensorChanged 方法
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy){
...
mListener.onSensorChanged(t);
}
查看 AccelSensorJudge的onSensorChanged 方法,会经过一系列判断,如果判断到方向跟之前的方向不一致,则调用 OrientationListener的onProposedRotationChanged 方法
public void onSensorChanged(SensorEvent event) {
// Tell the listener.
if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
if (LOG) {
Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation
+ ", oldProposedRotation=" + oldProposedRotation);
}
onProposedRotationChanged(proposedRotation);
}}
OrientationListener 的onProposedRotationChanged 方法启动了一个任务
@Override
public void onProposedRotationChanged(int rotation) {
ProtoLog.v(WM_DEBUG_ORIENTATION, "onProposedRotationChanged, rotation=%d", rotation);
Runnable r = mRunnableCache.get(rotation, null);
if (r == null) {
r = new UpdateRunnable(rotation);
mRunnableCache.put(rotation, r);
}
getHandler().post(r);
}
该任务的 run 方法会调用 WMS的updateRotation,这样就开始进入屏幕旋转
public void run() {
// Send interaction power boost to improve redraw performance.
mService.mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0);
// 模式不是锁定屏幕的就会返回false,所以会执行下面updateRotation方法
// 传进去的是 mCurrentAppOrientation 变量,表示
if (isRotationChoicePossible(mCurrentAppOrientation)) {
final boolean isValid = isValidRotationChoice(mRotation);
sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);
} else {
// 执行旋转
mService.updateRotation(false /* alwaysSendConfiguration */,
false /* forceRelayout */);
}
}
2 旋转的判断,开始冻屏以及动画准备
updateRotation 又会调用 updateRotationUnchecked 方法。该方法主要
- 遍历调用RootWindowContainer的displayContent,调用 updateRotationUnchecked 判断是否需要旋转
- 当有变化的时候,DisplayRotation 对应mIsWaitingForRemoteRotation 变量也为true。
private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
final int displayCount = mRoot.mChildren.size();
for (int i = 0; i < displayCount; ++i) {
final DisplayContent displayContent = mRoot.mChildren.get(i);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");
// 进行对旋转的判断,判断是否需要旋转
final boolean rotationChanged = displayContent.updateRotationUnchecked();
...
// 因为 mIsWaitingForRemoteRotation,在通知sytemUi那会置为true,在回调回来才会变为false
// 所以 pendingRemoteRotation为true
final boolean pendingRemoteRotation = rotationChanged
&& (displayContent.getDisplayRotation().isWaitingForRemoteRotation()
|| displayContent.mTransitionController.isCollecting());
}
displayContent的updateRotationUnchecked,又会调用 mDisplayRotation的updateRotationUnchecked 方法
- 判断是否有没在动画过程,判断有则返回false
- 通过 mService.mDisplayFrozen 判断是否冻结
- 调用 rotationForOrientation 结合当前的app屏幕配置 和当前屏幕的旋转角度,计算对应旋转角度
- 如果计算出来和当前一样,则不处理
- 将计算出来的角度赋值给变量mRotation,这里更新了DisplayRotation的最新角度
- 标记 WMS 为冻屏,并启动一个延时任务
- 判断是否马上进行旋转,一般不是 会调用prepareNormalRotationAnimation 不是的话准备进退出动画资源,一般都为0非自定义
- 调用 startRemoteRotation 通知到systemUi,等待回调通知准备结束。
boolean updateRotationUnchecked(boolean forceUpdate) {
// 判断是否动画中
if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) {
...
ProtoLog.v(WM_DEBUG_ORIENTATION, "Deferring rotation, animation in progress.");
return false;
}
// 是否冻结
if (mService.mDisplayFrozen) {
...
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Deferring rotation, still finishing previous rotation");
return false;
}
...
// mRotation为旋转角度, mLastOrientation 为当前的app屏幕配置
final int oldRotation = mRotation;
final int lastOrientation = mLastOrientation;
// 计算出当前的旋转角度
final int rotation = rotationForOrientation(lastOrientation, oldRotation);
// 如果计算出来和当前一样,则不处理
if (oldRotation == rotation) {
// No change.
return false;
}
// 将要旋转的角度赋值给rotation
mRotation = rotation;
// 设置标记mLayoutNeeded为true
mDisplayContent.setLayoutNeeded();
// 标记为冻屏,并发送一个任务防止无变化2秒超时
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE;
mService.mH.sendNewMessageDelayed(WindowManagerService.H.WINDOW_FREEZE_TIMEOUT,
mDisplayContent, WINDOW_FREEZE_TIMEOUT_DURATION);
// 判断是否马上进行旋转,不是的话准备进退出动画资源,一般都为0非自定义
if (shouldRotateSeamlessly(oldRotation, rotation, forceUpdate)) {
// The screen rotation animation uses a screenshot to freeze the screen while windows
// resize underneath. When we are rotating seamlessly, we allow the elements to
// transition to their rotated state independently and without a freeze required.
prepareSeamlessRotation();
} else {
// 准备动画,并开始启动冻屏
prepareNormalRotationAnimation();
}
// Give a remote handler (system ui) some time to reposition things.
// 传入新的旋转角度和旧的角度,
// 会调用 DisplayRotationController.onRotateDisplay 给到systemUi
startRemoteRotation(oldRotation, mRotation);
}
2.1 旋转的判断
rotationForOrientation 方法
- 通过mOrientationListener.getProposedRotation() 获取传感器当前的角度。
- 计算要的角度,并保存到 preferredRotation变量。
- 最后根据这些app的orientation屏幕属性算出对应的实际需要角度
int rotationForOrientation(@ScreenOrientation int orientation,
@Surface.Rotation int lastRotation) {
...
// 获取传感器的角度
int sensorRotation = mOrientationListener != null
? mOrientationListener.getProposedRotation() // may be -1
: -1;
if (sensorRotation < 0) {
sensorRotation = lastRotation;
}
...
// 计算要的角度
final int preferredRotation;
if (!isDefaultDisplay) {
// For secondary displays we ignore things like displays sensors, docking mode and
// rotation lock, and always prefer user rotation.
preferredRotation = mUserRotation;
} else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED
&& orientation != ActivityInfo.SCREEN_ORIENTATION_NOSENSOR
&& orientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
&& orientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE
&& orientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
preferredRotation = mUserRotation;
}
// 最后根据这些app的orientation屏幕属性算出对应的实际需要角度
switch (orientation) {
case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
// Return portrait unless overridden.
if (isAnyPortrait(preferredRotation)) {
return preferredRotation;
}
return mPortraitRotation;
...
}
}
2.2 准备进退动画
prepareNormalRotationAnimation 主要
- 通过 selectRotationAnimation 方法,选出exit,enter 的动画资源ID,正常都没自定义动画,都是返回0
- 在调用 WMS 的 startFreezingDisplay , 开始冻结。
void prepareNormalRotationAnimation() {
cancelSeamlessRotation();
// 返回全屏窗口进入和退出动画,不是值为0
final RotationAnimationPair anim = selectRotationAnimation();
// 开启冻结屏幕
mService.startFreezingDisplay(anim.mExit, anim.mEnter, mDisplayContent);
}
2.3 屏幕冻结
startFreezingDisplay 方法主要
- 判断标志位mDisplayFrozen是否为true已冻结,是的话返回,否则 将标志位 mDisplayFrozen 置为true
- 将对应的进出动画资源保存下来
- 创建ScreenRotationAnimation动画管理类
- 调用 displayContent的setRotationAnimation 将创建的ScreenRotationAnimation 保存到 displayContent的mScreenRotationAnimation 中
void startFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
int overrideOriginalRotation) {
// 判断是否冻结了
if (mDisplayFrozen || displayContent.getDisplayRotation().isRotatingSeamlessly()) {
return;
}
....
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_FREEZE_DISPLAY);
// 设置标记为true和时间
mDisplayFrozen = true;
mDisplayFreezeTime = SystemClock.elapsedRealtime();
...
// 保存对应的进出动画
mExitAnimId = exitAnim;
mEnterAnimId = enterAnim;
...
final int originalRotation = overrideOriginalRotation != ROTATION_UNDEFINED
? overrideOriginalRotation
: displayContent.getDisplayInfo().rotation;
// ScreenRotationAnimation 构造方法里面会创建截图图层,设置旋转动画事务,传进入的角度还是没改变
// setRotationAnimation 将创建的赋值给变量mScreenRotationAnimation
displayContent.setRotationAnimation(new ScreenRotationAnimation(displayContent,
originalRotation));
}
2.4 通知systemUi,并等待回调
startRemoteRotation 会传入对应新旧角度
- 标记变量 mIsWaitingForRemoteRotation 为 true
- 调用 WMS 的DisplayRotationController 的 onRotateDisplay 方法,通知SystemUI 开始旋转。,并且传入参数为AIDL 的 mRemoteRotationCallback 。
- 回调接收到后,会通过handle 发送消息,触发 continueRotation 方法。
private void startRemoteRotation(int fromRotation, int toRotation) {
if (mService.mDisplayRotationController == null) {
return;
}
// 设置标记为为true
mIsWaitingForRemoteRotation = true;
try {
// mRemoteRotationCallback 是AIDL,对端执行完会触发该回调
mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(),
fromRotation, toRotation, mRemoteRotationCallback);
// 添加会移除的回调
mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
mService.mH.postDelayed(mDisplayRotationHandlerTimeout, REMOTE_ROTATION_TIMEOUT_MS);
} catch (RemoteException e) {
mIsWaitingForRemoteRotation = false;
return;
}
}
private final IDisplayWindowRotationCallback mRemoteRotationCallback =
new IDisplayWindowRotationCallback.Stub() {
@Override
public void continueRotateDisplay(int targetRotation,
WindowContainerTransaction t) {
synchronized (mService.getWindowManagerLock()) {
// 执行 continueRotation继续做旋转逻辑
mService.mH.sendMessage(PooledLambda.obtainMessage(
DisplayRotation::continueRotation, DisplayRotation.this,
targetRotation, t));
}
}
};
3 更新动画以及DisplayInfo
触发回调的 continueRotation 方法,该方法会触发 displayContent 的 sendNewConfiguration 方法。
private void continueRotation(int targetRotation, WindowContainerTransaction t) {
synchronized (mService.mGlobalLock) {
// 判断和当前的是否一致
if (targetRotation != mRotation || !mIsWaitingForRemoteRotation) {
// Drop it, this is either coming from an outdated remote rotation; or, we've
// already moved on.
return;
}
....
try {
// 更新相关DisplayInfo信息,包括rotation变量,和通知applicaiton和各个窗口触发onConfigurationChanged回调
mDisplayContent.sendNewConfiguration();
if (t != null) {
mService.mAtmService.mWindowOrganizerController.applyTransaction(t);
}
} finally {
mService.mAtmService.continueWindowLayout();
}
}
}
sendNewConfiguration 方法
- 会判断 isWaitingForRemoteRotation 方法是否在等待更新中
- 调用 updateDisplayOverrideConfigurationLocked 开始更新,并返回是否要更新的操作
void sendNewConfiguration() {
if (!isReady()) {
return;
}
// 这里也会判断
if (mDisplayRotation.isWaitingForRemoteRotation()) {
return;
}
// 在这里会判断是否需要更新,并更新对应的display信息
final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
if (configUpdated) {
return;
}
...
}
updateDisplayOverrideConfigurationLocked 方法
- 创建 Configuration 变量
- 将创建的 Configuration 变量 传进入 computeScreenConfiguration方法开始计算,计算后并更新到该变量
- 将更新好的变量传进 updateDisplayOverrideConfigurationLocked开始更新
boolean updateDisplayOverrideConfigurationLocked() {
...
// 创建新的Configuration
Configuration values = new Configuration();
// 开始将对应的信息放到values,并更新对应的数值
computeScreenConfiguration(values);
...
// 将对应的values,传进去,开始更新
updateDisplayOverrideConfigurationLocked(values, null /* starting */,
false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
}
3.1 更新DisplayInfo 和 Configuration信息
computeScreenConfiguration 方法主要用于计算对应的角度
- 调用updateDisplayAndOrientation ,更新对应的DisplayInfo信息,这时候更新displayInfo的rotation
- calculateBounds 计算显示范围 保存mTmpBounds
- 将其他信息更新 config 字段
void computeScreenConfiguration(Configuration config) {
// updateDisplayAndOrientation 更新对应的display信息,并返回
final DisplayInfo displayInfo = updateDisplayAndOrientation(config.uiMode, config);
// 计算,并设置到对应的config信息
calculateBounds(displayInfo, mTmpBounds);
config.windowConfiguration.setBounds(mTmpBounds);
config.windowConfiguration.setMaxBounds(mTmpBounds);
config.windowConfiguration.setWindowingMode(getWindowingMode());
config.windowConfiguration.setDisplayWindowingMode(getWindowingMode());
final int dw = displayInfo.logicalWidth;
final int dh = displayInfo.logicalHeight;
// 计算app显示的相关信息,涉及inset一些计算,并也赋值到config里面
computeScreenAppConfiguration(config, dw, dh, displayInfo.rotation, config.uiMode,
displayInfo.displayCutout);
...
}
updateDisplayAndOrientation方法通过DisplayRotation获取对应的旋转角度,然后更新给displayInfo,这里也更新了其他属性。
private DisplayInfo updateDisplayAndOrientation(int uiMode, Configuration outConfig) {
// Use the effective "visual" dimensions based on current rotation
// 从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;
...
// 这里更新对应displayInfo信息,角度宽高
mDisplayInfo.rotation = rotation;
....
// 设置矩阵宽高
mBaseDisplayRect.set(0, 0, dw, dh);
}
3.2 通知更新
updateDisplayOverrideConfigurationLocked 将对应的values,传进去,开始更新
- 调用 ATMS的 updateGlobalConfigurationLocked 方法,开始通知各个app
- 调用 ensureConfigAndVisibilityAfterUpdate ,deferResume为fasle 不延迟,获取当前最顶端的activityRecord进行通知更改
boolean updateDisplayOverrideConfigurationLocked(Configuration values,
ActivityRecord starting, boolean deferResume,
ActivityTaskManagerService.UpdateConfigurationResult result) {
...
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.
// 获取屏幕会走这里,调用ATMS的方法,开始通知各个app
changes = mAtmService.updateGlobalConfigurationLocked(values,
false /* initLocale */, false /* persistent */,
UserHandle.USER_NULL /* userId */);
} else {
changes = performDisplayOverrideConfigUpdate(values);
}
}
// deferResume为fasle 不延迟,获取当前最顶端的activityRecord进行通知更改
if (!deferResume) {
kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
}
}
...
}
updateGlobalConfigurationLocked 方法进行通知操作
- 调用 updateFrom 判断对应的配置跟当前是否改变,并赋值给mTempConfig ,并判断是否有改变,没改变返回0
- 打印对应改变
- 获取每个进程,调用对应appliciton的onConfigurationChanged进行通知,此时调用的WindowProcessController的onConfigurationChanged
- 发送广播通知,在AMS发送
- 从rootWindowContatiner 开始遍历,通知各个窗口
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId) {
mTempConfig.setTo(getGlobalConfiguration());
// 判断对应的配置跟当前是否改变,并赋值给mTempConfig
final int changes = mTempConfig.updateFrom(values);
if (changes == 0) {
return 0;
}
...
// 打印改变
Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
...
// 获取每个进程,调用对应appliciton的onConfigurationChanged
SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
for (int i = pidMap.size() - 1; i >= 0; i--) {
final int pid = pidMap.keyAt(i);
final WindowProcessController app = pidMap.get(pid);
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
+ "config %s", app.mName, mTempConfig);
// 这里调用每个进程的 onConfigurationChanged,在 updateConfiguration,会调用通知唤醒客户端
app.onConfigurationChanged(mTempConfig);
}
// 发送广播通知,在AMS发送
final Message msg = PooledLambda.obtainMessage(
ActivityManagerInternal::broadcastGlobalConfigurationChanged,
mAmInternal, changes, initLocale);
mH.sendMessage(msg);
// Update stored global config and notify everyone about the change.
// 从rootWindowContatiner 开始遍历,通知各个窗口
mRootWindowContainer.onConfigurationChanged(mTempConfig);
return changes;
}
3.2.1 Application的通知
WindowProcessController 的onConfigurationChanged 如下,关键会调用 updateConfiguration 方法
@Override
public void onConfigurationChanged(Configuration newGlobalConfig) {
// 因为没子类,所以不会dispatchConfigurationToChild
super.onConfigurationChanged(newGlobalConfig);
updateConfiguration();
}
而 updateConfiguration 又会调 dispatchConfiguration
private void updateConfiguration() {
...
// 调用 dispatchConfiguration 开始进行分发
dispatchConfiguration(config);
}
dispatchConfiguration 也是调用了 clinet的ConfigurationChangeItem。该方法会通知各个进程的application的 ConfigurationChange方法。
void dispatchConfiguration(Configuration config) {
mHasPendingConfigurationChange = false;
...
try {
config.seq = mAtm.increaseConfigurationSeqLocked();
// 调用 ConfigurationChangeItem 开始通知
mAtm.getLifecycleManager().scheduleTransaction(mThread,
ConfigurationChangeItem.obtain(config));
setLastReportedConfiguration(config);
} catch (Exception e) {
Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
}
}
3.2.2 容器的大小通知
容器的通知从 rootWindowContatiner 的onConfigurationChanged开始,该super方法在 ConfigurationContainer 中,在里面调用 dispatchConfigurationToChild。
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
// 在super方法进行处理,里面会调用 dispatchConfigurationToChild
super.onConfigurationChanged(newParentConfig);
updateSurfacePositionNonOrganized();
scheduleAnimation();
}
而 基类ConfigurationContainer的 onConfigurationChanged 会调用 dispatchConfigurationToChild,此时遍历的孩子为displayContent。
public void onConfigurationChanged(Configuration newParentConfig) {
...
// 调用 开始遍历每个子类,RootWindowContainer有重写dispatchConfigurationToChild 调用dispatchConfigurationToChild进分分发
for (int i = getChildCount() - 1; i >= 0; --i) {
dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
}
}
所以该方法又等于调用 rootWindowContatiner的 dispatchConfigurationToChild,如果是默认的屏幕的话 会走
performDisplayOverrideConfigUpdate 方法,child 会displayContent.
@Override
void dispatchConfigurationToChild(DisplayContent child, Configuration config) {
if (child.isDefaultDisplay) {
// The global configuration is also the override configuration of default display.
// 默认屏幕走 performDisplayOverrideConfigUpdate,调用displayContent的performDisplayOverrideConfigUpdate
child.performDisplayOverrideConfigUpdate(config);
} else {
child.onConfigurationChanged(config);
}
}
performDisplayOverrideConfigUpdate 会判断是否有改变,有改变的话 调用 onRequestedOverrideConfigurationChanged,通知
int performDisplayOverrideConfigUpdate(Configuration values) {
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);
// 开始通知
onRequestedOverrideConfigurationChanged(mTempConfig);
...
}
return changes;
}
onRequestedOverrideConfigurationChanged 主要内容
- 通过 getRequestedOverrideConfiguration 获取当前的配置
- 从 overrideConfiguration获取要旋转的角度
- 判断是否角度改变,,调用 applyRotation 重新设置矩阵,这时候角度变了。applyRotationAndFinishFixedRotation 会调用 applyRotation 方法。然后获取之前构建的 ScreenRotationAnimation,在调用 他的 setRotation,更新矩阵
- 调用基类WindowContainer的方法判断显示区域是否变化,变化则执行onResize
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// 获取的是当前的
final Configuration currOverrideConfig = getRequestedOverrideConfiguration();
final int currRotation = currOverrideConfig.windowConfiguration.getRotation();
final int overrideRotation = overrideConfiguration.windowConfiguration.getRotation();
// 对比下角度,旋转不相等,调用 applyRotation 重新设置矩阵,这时候角度变了
if (currRotation != ROTATION_UNDEFINED && overrideRotation != ROTATION_UNDEFINED
&& currRotation != overrideRotation) {
applyRotationAndFinishFixedRotation(currRotation, overrideRotation);
}
mCurrentOverrideConfigurationChanges = currOverrideConfig.diff(overrideConfiguration);
// 调用基类WindowContainer的方法判断显示区域是否变化,变化则执行onResize
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
mCurrentOverrideConfigurationChanges = 0;
mWmService.setNewDisplayOverrideConfiguration(currOverrideConfig, this);
mAtmService.addWindowLayoutReasons(
ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
}
applyRotationAndFinishFixedRotation 方法会调用 applyRotation
private void applyRotationAndFinishFixedRotation(int oldRotation, int newRotation) {
final WindowToken rotatedLaunchingApp = mFixedRotationLaunchingApp;
if (rotatedLaunchingApp == null) {
applyRotation(oldRotation, newRotation);
return;
}
rotatedLaunchingApp.finishFixedRotationTransform(
() -> applyRotation(oldRotation, newRotation));
setFixedRotationLaunchingAppUnchecked(null);
}
调用 ScreenRotationAnimation 的setRotation设置传入的角度为新的角度了,所以可以更新最新的变换矩阵
private void applyRotation(final int oldRotation, final int rotation) {
...
// startFreezingDisplay 的时候已经构建了screenRotationAnimation
ScreenRotationAnimation screenRotationAnimation = rotateSeamlessly
? null : getRotationAnimation();
...
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
// 再次调用setRotation设置变换矩阵
screenRotationAnimation.setRotation(transaction, rotation);
}
...
}
WindowContainer 的onRequestedOverrideConfigurationChanged方法,
- 又会调用ConfigurationContainer的onRequestedOverrideConfigurationChanged方法, 又会调用 onConfigurationChanged 方法,
- 会判断是否有大小变化,有的话执行onResize 通知。
// WindowContainer 的 onRequestedOverrideConfigurationChanged
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
...
// 又会调用ConfigurationContainer的方法
super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
if (mParent != null) {
mParent.onDescendantOverrideConfigurationChanged();
}
if (diff == BOUNDS_CHANGE_NONE) {
return;
}
// 又变化执行size
if ((diff & BOUNDS_CHANGE_SIZE) == BOUNDS_CHANGE_SIZE) {
onResize();
} else {
onMovedByResize();
}
}
// ConfigurationContainer 的方法
public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
// Pre-compute this here, so we don't need to go through the entire Configuration when
// writing to proto (which has significant cost if we write a lot of empty configurations).
mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration);
mRequestedOverrideConfiguration.setTo(overrideConfiguration);
final Rect newBounds = mRequestedOverrideConfiguration.windowConfiguration.getBounds();
if (mHasOverrideConfiguration && providesMaxBounds()
&& diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) {
mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds);
}
// Update full configuration of this container and all its children.
final ConfigurationContainer parent = getParent();
// 执行onConfigurationChanged,遍历子viewdispatchConfigurationToChild
onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY);
}
经过循环都会调用到 windoState的 onResize 方法。
- 当有Surface的时候,并且可见,添加到 WMS的 resizingWindows 数组中。
void onResize() {
final ArrayList<WindowState> resizingWindows = mWmService.mResizingWindows;
// 当dispatchConfigchange添加到MWS的 resizingWindows
if (mHasSurface && !isGoneForLayout() && !resizingWindows.contains(this)) {
ProtoLog.d(WM_DEBUG_RESIZE, "onResize: Resizing %s", this);
resizingWindows.add(this);
}
if (isGoneForLayout()) {
mResizedWhileGone = true;
}
super.onResize();
}
添加到数组后,当 界面重新绘制 执行 performSurfacePlacementNoTrace 的方法时候。会执行 handleResizingWindows 方法。将数组的获取出来,然后执行 WindowState 的 reportResized 方法。
private void handleResizingWindows() {
for (int i = mWmService.mResizingWindows.size() - 1; i >= 0; i--) {
WindowState win = mWmService.mResizingWindows.get(i);
if (win.mAppFreezing || win.getDisplayContent().mWaitingForConfig) {
// Don't remove this window until rotation has completed and is not waiting for the
// complete configuration.
continue;
}
win.reportResized();
mWmService.mResizingWindows.remove(i);
}
}
reportResized 方法,首先会判断是否重启,如果是重启则不需要通知了,不然则调用 viewRootImpl 添加一开始的W类通知viewRootImpl 执行resized方法。
void reportResized() {
// If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
// since it will be destroyed anyway. This also prevents the client from receiving
// windowing mode change before it is destroyed.
// 如果是重启的则不需要了
if (mActivityRecord != null && mActivityRecord.isRelaunching()) {
return;
}
....
try {
// 调用viewRootImpl的W类,通知到viewRootImpl那边
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
forceRelayout, alwaysConsumeSystemBars, displayId);
if (drawPending && reportOrientation && mOrientationChanging) {
mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Requested redraw for orientation change: %s", this);
}
...
}
}
3.3 Activity界面更新
上面 DisplayContent 的 updateDisplayOverrideConfigurationLocked 执行后,除了执行 ATMS 的updateGlobalConfigurationLocked 后 还会执行 ensureConfigAndVisibilityAfterUpdate。
- 通过 getTopDisplayFocusedRootTask 获取最顶端的Task
- 如果有变化,通过 topRunningActivity 获取对应的 ActivityRecord
- 执行 ensureActivityConfiguration 开始更新 ActivityRecord 界面
boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
// 获取对定的堆栈
final Task mainRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
// mainRootTask is null during startup.
if (mainRootTask != null) {
if (changes != 0 && starting == null) {
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
// 有打开activity,并且有改变,获取对应的 ActivityRecord
starting = mainRootTask.topRunningActivity();
}
if (starting != null) {
// 调用方法进行通知
kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mRootWindowContainer.ensureActivitiesVisible(starting, changes,
!PRESERVE_WINDOWS);
}
}
return kept;
}
ensureActivitiesVisible 方法主要判断
- 通过 shouldRelaunchLocked 判断是否需要重新加载Activity.
- 如果需要 重建,则执行 startFreezingScreenLocked,开始冻结显示
- 在调用 relaunchActivityLocked 进行重建通知。
- 如果没变化 则调用 scheduleConfigurationChanged 触发对应的 configurationChanged
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreVisibility) {
...
// 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);
// 这里开始判断是否要relaunch操作
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
// 开始冻结对应的显示
startFreezingScreenLocked(globalChanges);
forceNewConfig = false;
...
// 执行relaunch和resume等操作
relaunchActivityLocked(preserveWindow);
}
// All done... tell the caller we weren't able to keep this activity around.
return false;
}
...
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
// 没重建的时候,通知客户端ActivityConfigurationChangeItem,会根据传过去的config更新宽高等
scheduleConfigurationChanged(newMergedOverrideConfig);
}
stopFreezingScreenLocked(false);
return true;
}
shouldRelaunchLocked 方法主要获取对应配置文件配置,从 ActivityInfo 获取对应的 configChanged,在用当前的变化跟配置取反与,得出是否不等于0,表示要重载该界面。
private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
int configChanged = info.getRealConfigChanged();
....
// 从activityInfo获取config配置的标志位取反跟有改变的标志位进行与,大于0则表示是因为没配置所以要变更
// 得出这次变化是否要重启动activity
return (changes&(~configChanged)) != 0;
}
3.3.1 重建Activity
判断到需要 relaunch后,就会执行 startFreezingScreenLocked 方法。一直调用则会调用到 startFreezingScreen 方法。
- 将变量 mFreezingScreen 设置 true
- 注册WMS registerAppFreezeListener 对应的监听
- 对 WMS 的 mAppsFreezingScreen 进行递增,只有为0的时候,才会解冻。
- 当mAppsFreezingScreen为1的时候,也会调用 WMS的startFreezingDisplay 开始冻结屏幕
- 在遍历下面的子容器的 onStartFreezingScreen 方法。将 各个容器的变量 设置为 mAppFreezing 为true
void startFreezingScreen(int overrideOriginalDisplayRotation) {
...
ProtoLog.i(WM_DEBUG_ORIENTATION,
"Set freezing of %s: visible=%b freezing=%b visibleRequested=%b. %s",
appToken, isVisible(), mFreezingScreen, mVisibleRequested,
new RuntimeException().fillInStackTrace());
...
final boolean forceRotation = overrideOriginalDisplayRotation != ROTATION_UNDEFINED;
if (!mFreezingScreen) {
// 进行冻结 ,将 mFreezingScreen 设置为true
mFreezingScreen = true;
mWmService.registerAppFreezeListener(this);
// 对wms进行++
mWmService.mAppsFreezingScreen++;
if (mWmService.mAppsFreezingScreen == 1) {
if (forceRotation) {
// Make sure normal rotation animation will be applied.
mDisplayContent.getDisplayRotation().cancelSeamlessRotation();
}
mWmService.startFreezingDisplay(0 /* exitAnim */, 0 /* enterAnim */,
mDisplayContent, overrideOriginalDisplayRotation);
mWmService.mH.removeMessages(H.APP_FREEZE_TIMEOUT);
mWmService.mH.sendEmptyMessageDelayed(H.APP_FREEZE_TIMEOUT, 2000);
}
}
if (forceRotation) {
// The rotation of the real display won't change, so in order to unfreeze the screen
// via {@link #checkAppWindowsReadyToShow}, the windows have to be able to call
// {@link WindowState#reportResized} (it is skipped if the window is freezing) to update
// the drawn state.
return;
}
// 遍历下面的容器
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
final WindowState w = mChildren.get(i);
w.onStartFreezingScreen();
}
}
冻结后,执行 relaunchActivityLocked 进行Activity的重建。该方法
- 调用 startRelaunching 进行 标记。
- 创建 ActivityRelaunchItem 通知 Activity进行重建
- 根据情况调用 ResumeActivityItem 或者 PauseActivityItem 进行通知
void relaunchActivityLocked(boolean preserveWindow) {
...
try {
ProtoLog.i(WM_DEBUG_STATES, "Moving to %s Relaunching %s callers=%s" ,
(andResume ? "RESUMED" : "PAUSED"), this, Debug.getCallers(6));
forceNewConfig = false;
startRelaunching();
// 调用 ActivityRelaunchItem 通知界面端重建
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(getProcessGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
// 判断是否要通知哪个生命周期
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(isTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
final ClientTransaction transaction = ClientTransaction.obtain(app.getThread(), appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
} catch (RemoteException e) {
ProtoLog.i(WM_DEBUG_STATES, "Relaunch failed %s", e);
}
}
3.3.2 非重建
没重建的时候,调用 scheduleConfigurationChanged ,通知客户端ActivityConfigurationChangeItem,会根据传过去的config更新宽高等
private void scheduleConfigurationChanged(Configuration config) {
if (!attachedToProcess()) {
ProtoLog.w(WM_DEBUG_CONFIGURATION, "Can't report activity configuration "
+ "update - client not running, activityRecord=%s", this);
return;
}
try {
ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending new config to %s, "
+ "config: %s", this, config);
mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
ActivityConfigurationChangeItem.obtain(config));
} catch (RemoteException e) {
// If process died, whatever.
}
}
4 动画的播放
4.1 停止冻屏
动画的播放 在 WMS的stopFreezingDisplayLocked 执行,具体执行可查看最后的堆栈打印。可以看到 触发停止冻屏是在 performSurfacePlacementNoTrace 方法触发。会根据 mOrientationChangeComplete 变量判断是否完成。
mOrientationChangeComplete 变量的赋值时机
- 当执行WindowAnimator的animate 动画时候会变为true
- 当执行 windowState的setOrientationChanging 会变为false
- 当对应需要改变的 WindowAnimator的prepareSurfaceLocked会判断是否需要改变并且还没绘制完成设置为false
void performSurfacePlacementNoTrace() {
// 根据变量mOrientationChangeComplete 判断是否要停止冻屏
// 该方法从 performSurfacePlacementNoTrace
// mOrientationChangeComplete 发生改变的时机有3个
// 1.当执行WindowAnimator的animate 动画时候会变为true
// 2.当执行 windowState的setOrientationChanging 会变为false
// 3.当对应需要改变的 WindowAnimator的prepareSurfaceLocked会判断是否需要改变并且还没绘制完成设置为false
if (mOrientationChangeComplete) {
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
mWmService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
mWmService.mLastFinishedFreezeSource = mLastWindowFreezeSource;
mWmService.mH.removeMessages(WINDOW_FREEZE_TIMEOUT);
}
mWmService.stopFreezingDisplayLocked();
}
}
执行动画的时候,变为 true
private void animate(long frameTimeNs, long vsyncId) {
...
final RootWindowContainer root = mService.mRoot;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = 0;
// 执行动画时候,变为true
root.mOrientationChangeComplete = true;
...
}
当需要旋转的时候设置为false
void setOrientationChanging(boolean changing) {
mOrientationChangeTimedOut = false;
if (mOrientationChanging == changing) {
return;
}
mOrientationChanging = changing;
if (changing) {
mLastFreezeDuration = 0;
if (mWmService.mRoot.mOrientationChangeComplete
&& mDisplayContent.waitForUnfreeze(this)) {
// 需要旋转的时候变为false
mWmService.mRoot.mOrientationChangeComplete = false;
}
} else {
// The orientation change is completed. If it was hidden by the animation, reshow it.
mDisplayContent.finishFadeRotationAnimation(this);
}
}
当WindowStateAnimator 动画在准备的时候,
- 判断对应的容器是否旋转并且还没绘制好的时候
- 通过 waitForUnfreeze 里面isHandledToken会判断token是否为底部栏,状态栏,忽略该视图
- // 将当前的windowState 放到 mLastWindowFreezeSource 变量,标记是因为哪个windowState导致冻屏
void prepareSurfaceLocked(SurfaceControl.Transaction t) {
// 判断屏幕是否旋转中
if (w.getOrientationChanging()) {
if (!w.isDrawn()) { // 还没绘制好的的时候
// 里面isHandledToken会判断token是否为底部栏,状态栏
if (w.mDisplayContent.waitForUnfreeze(w)) {
w.mWmService.mRoot.mOrientationChangeComplete = false;
// 将当前的windowState 放到 mLastWindowFreezeSource 变量
// 标记是因为哪个windowState导致冻屏
mAnimator.mLastWindowFreezeSource = w;
}
ProtoLog.v(WM_DEBUG_ORIENTATION,
"Orientation continue waiting for draw in %s", w);
} else {
w.setOrientationChanging(false);
ProtoLog.v(WM_DEBUG_ORIENTATION, "Orientation change complete in %s", w);
}
}
}
wms的 stopFreezingDisplayLocked 方法,会有一系列变量的判断
- mDisplayFrozen 判断是否有启动冻屏
- 判断 waitingForRemoteRotation以及mAppsFreezingScreen是否大于0,只有等于0 才执行
- 打印解冻的日志
- 调用 screenRotationAnimation的dismiss 方法开始播放动画,并调用 apply 应用。
void stopFreezingDisplayLocked() {
if (!mDisplayFrozen) {
return;
}
....
// 判断 waitingForRemoteRotation以及mAppsFreezingScreen是否大于0,只有等于0 才执行
if (waitingForConfig || waitingForRemoteRotation || mAppsFreezingScreen > 0
|| mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE
|| mClientFreezingScreen || numOpeningApps > 0) {
ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning "
+ "waitingForConfig=%b, waitingForRemoteRotation=%b, "
+ "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, "
+ "mClientFreezingScreen=%b, mOpeningApps.size()=%d",
waitingForConfig, waitingForRemoteRotation,
mAppsFreezingScreen, mWindowsFreezingScreen,
mClientFreezingScreen, numOpeningApps);
return;
}
....
// 打印冻结的原因
StringBuilder sb = new StringBuilder(128);
sb.append("Screen frozen for ");
TimeUtils.formatDuration(mLastDisplayFreezeDuration, sb);
// 打印对应的原因
if (mLastFinishedFreezeSource != null) {
sb.append(" due to ");
sb.append(mLastFinishedFreezeSource);
}
ProtoLog.i(WM_ERROR, "%s", sb.toString());
....
if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
ProtoLog.i(WM_DEBUG_ORIENTATION, "**** Dismissing screen rotation animation");
DisplayInfo displayInfo = displayContent.getDisplayInfo();
// Get rotation animation again, with new top window
if (!displayContent.getDisplayRotation().validateRotationAnimation(
mExitAnimId, mEnterAnimId, false /* forceDefault */)) {
mExitAnimId = mEnterAnimId = 0;
}
// 调用dismiss播放动画
if (screenRotationAnimation.dismiss(mTransaction, MAX_ANIMATION_DURATION,
getTransitionAnimationScaleLocked(), displayInfo.logicalWidth,
displayInfo.logicalHeight, mExitAnimId, mEnterAnimId)) {
mTransaction.apply();
}
}
4.1 播放动画
dismiss的方法又会调用 startAnimation 开始了播放动画。
public boolean dismiss(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
...
if (!mStarted) {
mEndLuma = RotationAnimationUtils.getLumaOfSurfaceControl(mDisplayContent.getDisplay(),
mDisplayContent.getWindowingLayer());
// 启动动画
startAnimation(t, maxAnimationDuration, animationScale, finalWidth, finalHeight,
exitAnim, enterAnim);
}
...
return true;
}
startAnimation 方法主要逻辑
- 通过 deltaRotation 计算差值
- 根据exitAnim 和enterAnim 获取进场退出动画, 一般exitAnim 和enterAnim都为0,赋值到 mRotateExitAnimation,mRotateEnterAnimation
- 对 进出动画进行设置,包含时长等。
- 调用 mSurfaceRotationAnimationController的startScreenRotationAnimation 开始旋转动画
private boolean startAnimation(SurfaceControl.Transaction t, long maxAnimationDuration,
float animationScale, int finalWidth, int finalHeight, int exitAnim, int enterAnim) {
// 计算差值
int delta = deltaRotation(mCurRotation, mOriginalRotation);
final boolean customAnim;
// 据exitAnim 和enterAnim 获取进场退出动画,一般exitAnim 和enterAnim都为0
// 赋值到 mRotateExitAnimation,mRotateEnterAnimation
if (exitAnim != 0 && enterAnim != 0) {
customAnim = true;
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext, exitAnim);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext, enterAnim);
mRotateAlphaAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_alpha);
} else {
customAnim = false;
switch (delta) { /* Counter-Clockwise Rotations */
case Surface.ROTATION_0:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_0_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.rotation_animation_enter);
break;
case Surface.ROTATION_90:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_plus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_plus_90_enter);
break;
case Surface.ROTATION_180:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_180_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_180_enter);
break;
case Surface.ROTATION_270:
mRotateExitAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_minus_90_exit);
mRotateEnterAnimation = AnimationUtils.loadAnimation(mContext,
R.anim.screen_rotate_minus_90_enter);
break;
}
}
ProtoLog.d(WM_DEBUG_ORIENTATION, "Start rotation animation. customAnim=%s, "
+ "mCurRotation=%s, mOriginalRotation=%s",
customAnim, Surface.rotationToString(mCurRotation),
Surface.rotationToString(mOriginalRotation));
// 设置对应的时间
mRotateExitAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateExitAnimation.restrictDuration(maxAnimationDuration);
mRotateExitAnimation.scaleCurrentDuration(animationScale); // 设置缩放
mRotateEnterAnimation.initialize(finalWidth, finalHeight, mOriginalWidth, mOriginalHeight);
mRotateEnterAnimation.restrictDuration(maxAnimationDuration);
mRotateEnterAnimation.scaleCurrentDuration(animationScale);
...
...
// 旋转动画为false
if (customAnim) {
mSurfaceRotationAnimationController.startCustomAnimation();
} else {
// 这里开始旋转动画
mSurfaceRotationAnimationController.startScreenRotationAnimation();
}
return true;
}
startScreenRotationAnimation 方法分别
- 调用 startDisplayRotation 启动屏幕旋转动画
- 调用 startScreenshotRotationAnimation 启动截屏动画
void startScreenRotationAnimation() {
try {
mService.mSurfaceAnimationRunner.deferStartingAnimations();
mDisplayAnimator = startDisplayRotation(); // 启动屏幕旋转动画
mScreenshotRotationAnimator = startScreenshotRotationAnimation(); //启动截屏
startColorAnimation();
} finally {
mService.mSurfaceAnimationRunner.continueStartingAnimations();
}
}
startDisplayRotation 分别设置 displayContent的图层和SurfaceControl,然后调用 startAnimation 方法
private SurfaceAnimator startDisplayRotation() {
return startAnimation(initializeBuilder()
.setAnimationLeashParent(mDisplayContent.getSurfaceControl())
.setSurfaceControl(mDisplayContent.getWindowingLayer()) // 设置DisplayContent的图层
.setParentSurfaceControl(mDisplayContent.getSurfaceControl())// 设置DisplayContent的SurfaceControl
.setWidth(mDisplayContent.getSurfaceWidth())
.setHeight(mDisplayContent.getSurfaceHeight())
.build(),
createWindowAnimationSpec(mRotateEnterAnimation),
this::onAnimationEnd);
}
startScreenshotRotationAnimation 通过 setAnimationLeashParent 设置图层为屏幕的,在构造的时候可以挂载到上面,然后调用 startAnimation 方法
private SurfaceAnimator startScreenshotRotationAnimation() {
// setAnimationLeashParent 设置图层为屏幕的,在构造的时候可以挂载到上面
return startAnimation(initializeBuilder()
.setSurfaceControl(mScreenshotLayer)
.setAnimationLeashParent(mDisplayContent.getOverlayLayer())
.build(),
createWindowAnimationSpec(mRotateExitAnimation),
this::onAnimationEnd);
}
startAnimation
- 会先创建对应SurfaceAnimator,因为他不像windowState已经拥有
- 创建本地动画LocalAnimationAdapter
- 在调用 animator的 startAnimation进行动画播放,后续的动画播放可以参考本文《frameworks 之 Splash开屏动画》
private SurfaceAnimator startAnimation(
SurfaceAnimator.Animatable animatable,
LocalAnimationAdapter.AnimationSpec animationSpec,
OnAnimationFinishedCallback animationFinishedCallback) {
// 创造对应的SurfaceAnimator,不像windowState的
SurfaceAnimator animator = new SurfaceAnimator(
animatable, animationFinishedCallback, mService);
// 创建本地动画
LocalAnimationAdapter localAnimationAdapter = new LocalAnimationAdapter(
animationSpec, mService.mSurfaceAnimationRunner);
animator.startAnimation(mDisplayContent.getPendingTransaction(),
localAnimationAdapter, false, ANIMATION_TYPE_SCREEN_ROTATION);
return animator;
}
在创建图层createAnimationLeash 的时候的时候就会 获取对应的**animatable.getAnimationLeashParent()**挂载到对应的图层上。
这样屏幕旋转的动画就完成了。
5 堆栈
5.1 更新当前app屏幕配置
updateOrientation:377, DisplayRotation (com.android.server.wm)
updateOrientation:1619, DisplayContent (com.android.server.wm)
updateOrientation:1566, DisplayContent (com.android.server.wm)
ensureVisibilityAndConfig:1867, RootWindowContainer (com.android.server.wm)
resumeTopActivity:1244, TaskFragment (com.android.server.wm)
resumeTopActivityInnerLocked:5037, Task (com.android.server.wm)
resumeTopActivityUncheckedLocked:4971, Task (com.android.server.wm)
resumeTopActivityUncheckedLocked:4985, Task (com.android.server.wm)
resumeFocusedTasksTopActivities:2399, RootWindowContainer (com.android.server.wm)
resumeTargetRootTaskIfNeeded:2788, ActivityStarter (com.android.server.wm)
recycleTask:2113, ActivityStarter (com.android.server.wm)
startActivityInner:1762, ActivityStarter (com.android.server.wm)
startActivityUnchecked:1594, ActivityStarter (com.android.server.wm)
executeRequest:1195, ActivityStarter (com.android.server.wm)
execute:674, ActivityStarter (com.android.server.wm)
startHomeActivity:179, ActivityStartController (com.android.server.wm)
startHomeOnTaskDisplayArea:1605, RootWindowContainer (com.android.server.wm)
lambda$startHomeOnDisplay$12$RootWindowContainer:1546, RootWindowContainer (com.android.server.wm)
apply:-1, RootWindowContainer$$ExternalSyntheticLambda10 (com.android.server.wm)
reduceOnAllTaskDisplayAreas:554, TaskDisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:392, DisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:392, DisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:392, DisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:392, DisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:392, DisplayArea (com.android.server.wm)
reduceOnAllTaskDisplayAreas:2091, WindowContainer (com.android.server.wm)
startHomeOnDisplay:1545, RootWindowContainer (com.android.server.wm)
startHomeOnDisplay:5842, ActivityTaskManagerService$LocalService (com.android.server.wm)
startDockOrHome:5106, PhoneWindowManager (com.android.server.policy)
startDockOrHome:5111, PhoneWindowManager (com.android.server.policy)
launchHomeFromHotKey:3254, PhoneWindowManager (com.android.server.policy)
launchHomeFromHotKey:3211, PhoneWindowManager (com.android.server.policy)
handleShortPressOnHome:1351, PhoneWindowManager (com.android.server.policy)
access$2200:240, PhoneWindowManager (com.android.server.policy)
lambda$handleHomeButton$0$PhoneWindowManager$DisplayHomeButtonHandler:1501, PhoneWindowManager$DisplayHomeButtonHandler (com.android.server.policy)
run:-1, PhoneWindowManager$DisplayHomeButtonHandler$$ExternalSyntheticLambda0 (com.android.server.policy)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
run:45, UiThread (com.android.server)
5.2 传感器上报
onSensorChanged:867, WindowOrientationListener$AccelSensorJudge (com.android.server.wm)
dispatchSensorEvent:886, SystemSensorManager$SensorEventQueue (android.hardware)
nativePollOnce:-1, MessageQueue (android.os)
next:335, MessageQueue (android.os)
loopOnce:161, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
run:45, UiThread (com.android.server)
5.3开始冻屏幕
startRemoteRotation:564, DisplayRotation (com.android.server.wm)
updateRotationUnchecked:536, DisplayRotation (com.android.server.wm)
updateRotationUnchecked:1962, DisplayContent (com.android.server.wm)
updateRotationUnchecked:4146, WindowManagerService (com.android.server.wm)
updateRotation:4127, WindowManagerService (com.android.server.wm)
run:1556, DisplayRotation$OrientationListener$UpdateRunnable (com.android.server.wm)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
run:45, UiThread (com.android.server)
startFreezingDisplay:5924, WindowManagerService (com.android.server.wm)
startFreezingScreen:6030, ActivityRecord (com.android.server.wm)
startFreezingScreen:6002, ActivityRecord (com.android.server.wm)
startFreezingScreenLocked:5997, ActivityRecord (com.android.server.wm)
startFreezingScreenLocked:5979, ActivityRecord (com.android.server.wm)
ensureActivityConfiguration:8535, ActivityRecord (com.android.server.wm)
ensureActivityConfiguration:8407, ActivityRecord (com.android.server.wm)
ensureConfigAndVisibilityAfterUpdate:4871, ActivityTaskManagerService (com.android.server.wm)
updateDisplayOverrideConfigurationLocked:5812, DisplayContent (com.android.server.wm)
updateDisplayOverrideConfigurationLocked:5780, DisplayContent (com.android.server.wm)
sendNewConfiguration:1479, DisplayContent (com.android.server.wm)
continueRotation:606, DisplayRotation (com.android.server.wm)
access$200:83, DisplayRotation (com.android.server.wm)
lambda$continueRotateDisplay$0:228, DisplayRotation$2 (com.android.server.wm)
accept:-1, DisplayRotation$2$$ExternalSyntheticLambda0 (com.android.server.wm)
doInvoke:295, PooledLambdaImpl (com.android.internal.util.function.pooled)
invoke:204, PooledLambdaImpl (com.android.internal.util.function.pooled)
run:97, OmniFunction (com.android.internal.util.function.pooled)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
5.4 创建旋转动画
createAnimationLeash:448, SurfaceAnimator (com.android.server.wm)
startAnimation:181, SurfaceAnimator (com.android.server.wm)
startAnimation:205, SurfaceAnimator (com.android.server.wm)
startAnimation:701, ScreenRotationAnimation$SurfaceRotationAnimationController (com.android.server.wm)
startDisplayRotation:584, ScreenRotationAnimation$SurfaceRotationAnimationController (com.android.server.wm)
startScreenRotationAnimation:568, ScreenRotationAnimation$SurfaceRotationAnimationController (com.android.server.wm)
startAnimation:427, ScreenRotationAnimation (com.android.server.wm)
dismiss:445, ScreenRotationAnimation (com.android.server.wm)
stopFreezingDisplayLocked:6043, WindowManagerService (com.android.server.wm)
performSurfacePlacementNoTrace:950, RootWindowContainer (com.android.server.wm)
performSurfacePlacement:823, RootWindowContainer (com.android.server.wm)
performSurfacePlacementLoop:184, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:130, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:115, WindowSurfacePlacer (com.android.server.wm)
run:57, WindowSurfacePlacer$Traverser (com.android.server.wm)
handleCallback:938, Handler (android.os)
dispatchMessage:99, Handler (android.os)
loopOnce:201, Looper (android.os)
loop:288, Looper (android.os)
run:67, HandlerThread (android.os)
run:44, ServiceThread (com.android.server)
5.5 冻结触发时机
stopFreezingDisplayLocked:5986, WindowManagerService (com.android.server.wm)
performSurfacePlacementNoTrace:957, RootWindowContainer (com.android.server.wm)
performSurfacePlacement:824, RootWindowContainer (com.android.server.wm)
performSurfacePlacementLoop:184, WindowSurfacePlacer (com.android.server.wm)
performSurfacePlacement:130, WindowSurfacePlacer (com.android.server.wm)
relayoutWindow:2430, WindowManagerService (com.android.server.wm)
relayout:252, Session (com.android.server.wm)
onTransact:745, IWindowSession$Stub (android.view)
onTransact:170, Session (com.android.server.wm)
execTransactInternal:1179, Binder (android.os)
execTransact:1143, Binder (android.os)