当前位置: 首页 > news >正文

Android 屏幕旋转流程

Android支持横屏和竖屏,用户可以选择锁定(rotation lock)也可以选择让传感器来自动转屏。而转屏时为了使用户体验更流畅,会对屏幕截屏,然后使用截屏的图来做转屏动画,直到转屏动作结束。

1. 调试方法

1.1. 打开debug log开关

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerDebugConfig.javastatic final String TAG_WM = "WindowManager";static final boolean DEBUG_ORIENTATION = false;  //truestatic final boolean DEBUG_APP_ORIENTATION = false;  //true

adb logcat -v threadtime|grep -Ei "rotation|ActivityTaskManager|WindowOrientationListener"

1.2. Settings设置开启/关闭自动旋转屏幕

是否要自动转屏是在Setting中设置的。为了监听Setting中的改动,系统启动时,PhoneWindowManager的init()函数中创建了SettingsObserver对象。

它的observe()方法会监听Settings.System.USER_ROTATION的值(Android Q中此处没有这个property了)。

 public void init(Context context, IWindowManager windowManager,WindowManagerFuncs windowManagerFuncs) {...mSettingsObserver = new SettingsObserver(mHandler);mSettingsObserver.observe();...

当用户在Setting中设置自动转屏后,会触发以下流程:

  1. public boolean onPreferenceTreeClick(Preference preference):packages/apps/Settings/src/com/android/settings/accessibility/AccessibilitySettings.java
  2. handleLockScreenRotationPreferenceClick():被调用
  3. setRotationLockForAccessibility(Context context, final boolean enabled)
//frameworks/base/core/java/com/android/internal/view/RotationPolicy.javapublic static void setRotationLockForAccessibility(Context context, final boolean enabled) {Settings.System.putIntForUser(context.getContentResolver(),Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,UserHandle.USER_CURRENT);setRotationLock(enabled, NATURAL_ROTATION);}
  1. setRotationLockForAccessibility
//frameworks/base/core/java/com/android/internal/view/RotationPolicy.javapublic static void setRotationLockForAccessibility(Context context, final boolean enabled) {Settings.System.putIntForUser(context.getContentResolver(),Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY, enabled ? 1 : 0,UserHandle.USER_CURRENT);setRotationLock(enabled, NATURAL_ROTATION);}
  1. setRotationLock(final boolean enabled, final int rotation)调用wm.freezeRotation或者wm.thawRotation
//frameworks/base/core/java/com/android/internal/view/RotationPolicy.javaprivate static void setRotationLock(final boolean enabled, final int rotation) {AsyncTask.execute(new Runnable() {@Overridepublic void run() {try {IWindowManager wm = WindowManagerGlobal.getWindowManagerService();if (enabled) {wm.freezeRotation(rotation);} else {wm.thawRotation();}} catch (RemoteException exc) {Log.w(TAG, "Unable to save auto-rotate setting");}}});}
  1. thawRotation(),此处在Android Q中有变化。
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void thawRotation() {if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,"thawRotation()")) {throw new SecurityException("Requires SET_ORIENTATION permission");}if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "thawRotation: mRotation="+ getDefaultDisplayRotation());long origId = Binder.clearCallingIdentity();try {mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE,777); // rot not used   free} finally {Binder.restoreCallingIdentity(origId);}updateRotationUnchecked(false, false);  //查看下面第十步分析}

freezeRotation函数,只是调用PhoneWindowManager的setUserRotationMode的参数不一样,这里是Locked,而thawRotation传下去的参数是free。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java@Overridepublic void freezeRotation(int rotation) {// TODO(multi-display): Track which display is rotated.if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION,"freezeRotation()")) {throw new SecurityException("Requires SET_ORIENTATION permission");}if (rotation < -1 || rotation > Surface.ROTATION_270) {throw new IllegalArgumentException("Rotation argument must be -1 or a valid "+ "rotation constant.");}final int defaultDisplayRotation = getDefaultDisplayRotation();if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "freezeRotation: mRotation="+ defaultDisplayRotation);long origId = Binder.clearCallingIdentity();try {mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,rotation == -1 ? defaultDisplayRotation : rotation);    //lock} finally {Binder.restoreCallingIdentity(origId);}updateRotationUnchecked(false, false);}
  1. setUserRotationMode(int mode, int rot):此处是设置property(即Settings数据库),然后会触发到上面初始化的mSettingsObserver对象的onChange函数。

触发监听SettingsObserver.onChange(), 其中主要调用了updateSettings()和updateRotation()两个函数。

简单地说,主要的工作是根据需要监听传感器数据,据此判断是否要转屏。如果需要就是对configuration的各种更新。过程中会冻结屏幕,同时截屏并以此作为转屏动画。另外还需要将新configuration传给AMS,广播该事件给需要的模块,同时App也会被调度来响应变更。

//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java// User rotation: to be used when all else fails in assigning an orientation to the device@Overridepublic void setUserRotationMode(int mode, int rot) {ContentResolver res = mContext.getContentResolver();// mUserRotationMode and mUserRotation will be assigned by the content observerif (mode == WindowManagerPolicy.USER_ROTATION_LOCKED) {Settings.System.putIntForUser(res,Settings.System.USER_ROTATION,rot,UserHandle.USER_CURRENT);Settings.System.putIntForUser(res,Settings.System.ACCELEROMETER_ROTATION,0,UserHandle.USER_CURRENT);} else {Settings.System.putIntForUser(res,Settings.System.ACCELEROMETER_ROTATION,1,UserHandle.USER_CURRENT);}}
......@Override public void onChange(boolean selfChange) {updateSettings();    //以下8,9步骤updateRotation(false);  //以下10-步骤}
......
public void updateSettings() {ContentResolver resolver = mContext.getContentResolver();boolean updateRotation = false;synchronized (mLock) {...int userRotationMode = Settings.System.getIntForUser(resolver,Settings.System.ACCELEROMETER_ROTATION, 0, UserHandle.USER_CURRENT) != 0 ?WindowManagerPolicy.USER_ROTATION_FREE :WindowManagerPolicy.USER_ROTATION_LOCKED;if (mUserRotationMode != userRotationMode) {mUserRotationMode = userRotationMode;updateRotation = true;updateOrientationListenerLp();  //传感器相关操作}

第一个函数updateSettings()如它的名字主要更新设置信息。

如果UserRotation(朝向信息,如Surface.ROTATION_0)和UserRotationMode(USER_ROTATION_FREE vs. USER_ROTATION_LOCKED)有更新,就设置标记updateRotation为true,表示接下去需要更新rotation相关信息。

此外,如果UserRotationMode的配置有变,由于需要传感器信息的配合,还需调用updateOrientationListenerLp()来设置或取消监听传感器。

这里假设设置为自动旋转,那么PhoneWindowManager会通过MyOrientationListener来监听传感器信息。MyOrientationListener是WindowOrientationListener的继承类。它的enable()函数中调用SensorManager提供的registerListener()接口来设置Sensor信息的listener。

  1. updateOrientationListenerLp():作用是enable和disable传感器

其中的mOrientationListener.enablemOrientationListener.disable是注册传感器回调和去除传感器回调。

void updateOrientationListenerLp() {if (!mOrientationListener.canDetectOrientation()) {// If sensor is turned off or nonexistent for some reasonreturn;}if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly+ ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation+ ", mOrientationSensorEnabled=" + mOrientationSensorEnabled+ ", mKeyguardDrawComplete=" + mKeyguardDrawComplete+ ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete);boolean disable = true;if (mScreenOnEarly && mAwake && ((mKeyguardDrawComplete && mWindowManagerDrawComplete))) {if (needSensorRunningLp()) {disable = false;//enable listener if not already enabled 启动传感器监听!!if (!mOrientationSensorEnabled) {mOrientationListener.enable(true /* clearCurrentRotation */);if(localLOGV) Slog.v(TAG, "Enabling listeners");mOrientationSensorEnabled = true;}}}//check if sensors need to be disabledif (disable && mOrientationSensorEnabled) {mOrientationListener.disable();   //关闭传感器if(localLOGV) Slog.v(TAG, "Disabling listeners");mOrientationSensorEnabled = false;}}
  1. mOrientationListener是MyOrientationListener对象,而MyOrientationListener类继承父类WindowOrientationListener,从而会调用父类的enable函数。

该函数中会调用registerListener向SensorManager注册一个监听。

//frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.javapublic void enable(boolean clearCurrentRotation) {synchronized (mLock) {if (mSensor == null) {Slog.w(TAG, "Cannot detect sensors. Not enabled");return;}if (mEnabled) {return;}if (LOG) {Slog.d(TAG, "WindowOrientationListener enabled clearCurrentRotation="+ clearCurrentRotation);}mOrientationJudge.resetLocked(clearCurrentRotation);if (mSensor.getType() == Sensor.TYPE_ACCELEROMETER) {mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, DEFAULT_BATCH_LATENCY, mHandler);   //mOrientationJudge的回调} else {mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);}mEnabled = true;}}

registerListener()的具体实现在frameworks/base/core/java/android/hardware/SensorManager.java中。

然后调用SystemSensorManager.java的registerListenerImpl(),其中会创建SensorEventQueue对象(基类为BaseEventQueue),它是传感器事件的队列,记录需要监听哪些传感器信息。

SensorEventQueue queue = mSensorListeners.get(listener);

同时它也负责与SensorService的连接和通信,可以说是SensorEventListener与SensorService间的桥梁。

SensorEventListener和SensorEventQueue之间是1:1的关系,它们的映射关系保存在成员mSensorListeners中。如果这里注册的SensorEventListener还没有相应的SensorEventQueue,则新建一个,然后通过addSensor()方法将要关注的传感器进行注册。这个过程中addSensor()调用了enableSensor(),它最终是通过SensorService的enableDisable()方法来完成注册工作的。

这样,SensorService就开始监听该Sensor,当底层有传感器数据来时,SensorService主线程中会调用相应SensorEventConnection的sendEvents()将之发给对应的Client。

前面初始化SensorEventQueue时会创建Receiver,它是一个Looper的回调对象,在Client端收到从SensorService来的数据后被回调。

当有数据收到时Receiver的handleEvent()被调用,继而通过JNI调用到SystemSensorManager::dispatchSensorEvent()。

接着就调到了WindowOrientationListener的onSensorChanged()函数。该函数计算是否需要转屏。如果需要转屏,将计算结果传给onProposedRotationChanged()。

比如以下函数的日志打印,在旋转手机,传感器会触发屏幕旋转打印这部分log:

```java frameworks/base/services/core/java/com/android/server/policy/WindowOrientationListener.java @Override 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); } }


***10. 另一处`updateRotation(false)`函数会调用到WMS.java,然后调用到`updateRotationUnchecked`函数。最终在该函数中调用`rotationChanged = displayContent.updateRotationUnchecked();`***# 2. 屏幕旋转假设现在用户转了屏幕,期望转屏事件发生。如上面第九步的代码,`onProposedRotationChanged()`被调用。最后就调用其run函数,run函数先会提升性能(cpu频率),然后调用了updateRotation,这个函数一样就到WMS的updateRotationUnchecked函数。```java
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
class MyOrientationListener extends WindowOrientationListener {
...private class UpdateRunnable implements Runnable {private final int mRotation;UpdateRunnable(int rotation) {mRotation = rotation;}@Overridepublic void run() {// send interaction hint to improve redraw performancemPowerManagerInternal.powerHint(PowerHint.INTERACTION, 0);if (isRotationChoicePossible(mCurrentAppOrientation)) {final boolean isValid = isValidRotationChoice(mCurrentAppOrientation,mRotation);sendProposedRotationChangeToStatusBarInternal(mRotation, isValid);} else {updateRotation(false);}}}@Overridepublic void onProposedRotationChanged(int rotation) {if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation);Runnable r = mRunnableCache.get(rotation, null);if (r == null){r = new UpdateRunnable(rotation);mRunnableCache.put(rotation, r);}mHandler.post(r);  //发送了一个消息}}

updateRotation()中主要是执行两个函数:updateRotationUnchecked()(displayContent.updateRotationUnchecked())和sendNewConfiguration()。前者执行转屏动作,包含转屏动画 等。后者使AMS获取当前新的configuration,并且广播该事件给所有相应的listener。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java@Overridepublic void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}//上面settings中设置自动旋转屏幕也会调用到(thawRotation和freezeRotation函数)private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {if(DEBUG_ORIENTATION) Slog.v(TAG_WM, "updateRotationUnchecked:"+ " alwaysSendConfiguration=" + alwaysSendConfiguration+ " forceRelayout=" + forceRelayout);...try {// TODO(multi-display): Update rotation for different displays separately.final boolean rotationChanged;final int displayId;synchronized (mWindowMap) {final DisplayContent displayContent = getDefaultDisplayContentLocked();Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");//Step 1rotationChanged = displayContent.updateRotationUnchecked();...}if (rotationChanged || alwaysSendConfiguration) {Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: sendNewConfiguration");//Step 2sendNewConfiguration(displayId);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}} finally {Binder.restoreCallingIdentity(origId);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}}

Note:: 其它途径可能会触发转屏,比如应用请求转屏。例如需要横屏的游戏(通过frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateOrientationFromAppTokensLocked()方法)。


2.1. updateRotationUnchecked函数

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
boolean updateRotationUnchecked(boolean forceUpdate) {final int oldRotation = mRotation;final int lastOrientation = mLastOrientation;final boolean oldAltOrientation = mAltOrientation;//先调用PhoneWindowManager的rotationForOrientationLw函数来获取rotation,然后与之前的mRotation对比是否有变化//没有变化直接返回false。有变化将mRotation重新赋值//函数rotationForOrientationLw作用:获取sensor的rotation,然后计算返回我们需要的rotationfinal int rotation = mService.mPolicy.rotationForOrientationLw(lastOrientation, oldRotation,isDefaultDisplay);if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Computed rotation=" + rotation + " for display id="+ mDisplayId + " based on lastOrientation=" + lastOrientation+ " and oldRotation=" + oldRotation);
...if (oldRotation == rotation && oldAltOrientation == altOrientation) {  //没有变化// No change.return false;}...mRotation = rotation;   //有变化则赋值mAltOrientation = altOrientation; ...updateDisplayAndOrientation(getConfiguration().uiMode);...mService.mDisplayManagerInternal.performTraversal(getPendingTransaction());...
}

2.1.1. updateDisplayAndOrientation函数

还会调用到updateDisplayAndOrientation函数,会把各种数据更新下放到DisplayInfo中,最后调用了DisplayManagerService的setDisplayInfoOverrideFromWindowManager函数。

//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.javaprivate DisplayInfo updateDisplayAndOrientation(int uiMode) {// Use the effective "visual" dimensions based on current rotationfinal boolean rotated = (mRotation == ROTATION_90 || mRotation == ROTATION_270);final int realdw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;final int realdh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;int dw = realdw;int dh = realdh;...final int appWidth = mService.mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation, uiMode,mDisplayId, displayCutout);final int appHeight = mService.mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation, uiMode,mDisplayId, displayCutout);mDisplayInfo.rotation = mRotation;mDisplayInfo.logicalWidth = dw;mDisplayInfo.logicalHeight = dh;mDisplayInfo.logicalDensityDpi = mBaseDisplayDensity;mDisplayInfo.appWidth = appWidth;mDisplayInfo.appHeight = appHeight;if (isDefaultDisplay) {mDisplayInfo.getLogicalMetrics(mRealDisplayMetrics,CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);}mDisplayInfo.displayCutout = displayCutout.isEmpty() ? null : displayCutout;mDisplayInfo.getAppMetrics(mDisplayMetrics);if (mDisplayScalingDisabled) {mDisplayInfo.flags |= Display.FLAG_SCALING_DISABLED;} else {mDisplayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;}...mService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(mDisplayId,overrideDisplayInfo);...    }

setDisplayInfoOverrideFromWindowManager会调用setDisplayInfoOverrideFromWindowManagerInternal,然后调用display.setDisplayInfoOverrideFromWindowManagerLocked(info)函数,最后到LogicalDisplay的setDisplayInfoOverrideFromWindowManagerLocked函数中,把DisplayInfo数据放到了mOverrideDisplayInfo中。

//frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.javapublic boolean setDisplayInfoOverrideFromWindowManagerLocked(DisplayInfo info) {if (info != null) {if (mOverrideDisplayInfo == null) {mOverrideDisplayInfo = new DisplayInfo(info);mInfo = null;return true;}if (!mOverrideDisplayInfo.equals(info)) {mOverrideDisplayInfo.copyFrom(info); //拷贝到mOverrideDisplayInfo中mInfo = null;return true;}} else if (mOverrideDisplayInfo != null) {mOverrideDisplayInfo = null;mInfo = null;return true;}return false;}

2.1.2. performTraversal处理显示Layer的大小宽高尺寸

调用到DisplayManagerService.java中,然后调用performTraversalInternal函数。

//frameworks/base/services/core/java/com/android/server/display/DisplayManagerService.javavoid performTraversalInternal(SurfaceControl.Transaction t) {synchronized (mSyncRoot) {if (!mPendingTraversal) {return;}mPendingTraversal = false;//遍历所有的DeviceperformTraversalLocked(t);}// List is self-synchronized copy-on-write.for (DisplayTransactionListener listener : mDisplayTransactionListeners) {listener.onDisplayTransaction();}}private void performTraversalLocked(SurfaceControl.Transaction t) {// Clear all viewports before configuring displays so that we can keep// track of which ones we have configured.clearViewportsLocked();// 遍历所有的Devicefinal int count = mDisplayDevices.size();for (int i = 0; i < count; i++) {DisplayDevice device = mDisplayDevices.get(i);//step 1:找到那个LogicalDisplay 然后调用其configureDisplayInTransactionLocked函数(看上面的将参数赋值到mOverrideDisplayInfo中)configureDisplayLocked(t, device);//step 2:调用了各个Device的performTraversalInTransactionLocked,而普通的Device的为空device.performTraversalLocked(t);}// Tell the input system about these new viewports.if (mInputManagerInternal != null) {mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT);}}private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {...//设置长宽,旋转角度等display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);...}

configureDisplayLocked函数的这部分代码就是设置layer的显示大小,例如viewport,通过Dump SF可以查看layer。

//frameworks/base/services/core/java/com/android/server/display/LogicalDisplay.java//这部分代码就是设置layer的显示大小,例如viewport,通过Dump SF可以查看layerpublic void configureDisplayLocked(SurfaceControl.Transaction t,DisplayDevice device,boolean isBlanked) {...//step 1. 获取mInfo的数据,而mOverrideDisplayInfo如有数据就要copy到mInfo中去final DisplayInfo displayInfo = getDisplayInfoLocked(); final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();// Set the viewport.// This is the area of the logical display that we intend to show on the// display device.  For now, it is always the full size of the logical display.mTempLayerStackRect.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);...mTempDisplayRect.left += mDisplayOffsetX;mTempDisplayRect.right += mDisplayOffsetX;mTempDisplayRect.top += mDisplayOffsetY;mTempDisplayRect.bottom += mDisplayOffsetY;//step 2device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect);}public DisplayInfo getDisplayInfoLocked() {if (mInfo == null) {mInfo = new DisplayInfo();mInfo.copyFrom(mBaseDisplayInfo);if (mOverrideDisplayInfo != null) {mInfo.appWidth = mOverrideDisplayInfo.appWidth;mInfo.appHeight = mOverrideDisplayInfo.appHeight;mInfo.smallestNominalAppWidth = mOverrideDisplayInfo.smallestNominalAppWidth;mInfo.smallestNominalAppHeight = mOverrideDisplayInfo.smallestNominalAppHeight;mInfo.largestNominalAppWidth = mOverrideDisplayInfo.largestNominalAppWidth;mInfo.largestNominalAppHeight = mOverrideDisplayInfo.largestNominalAppHeight;mInfo.logicalWidth = mOverrideDisplayInfo.logicalWidth;mInfo.logicalHeight = mOverrideDisplayInfo.logicalHeight;mInfo.overscanLeft = mOverrideDisplayInfo.overscanLeft;mInfo.overscanTop = mOverrideDisplayInfo.overscanTop;mInfo.overscanRight = mOverrideDisplayInfo.overscanRight;mInfo.overscanBottom = mOverrideDisplayInfo.overscanBottom;mInfo.rotation = mOverrideDisplayInfo.rotation;mInfo.displayCutout = mOverrideDisplayInfo.displayCutout;mInfo.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;}}return mInfo;}

setProjectionLocked会调用SurfaceControl的SurfaceControl函数。然后在SurfaceControl中调用nativeSetDisplayProjection函数,通过JNI调用到Native层。

//frameworks/base/services/core/java/com/android/server/display/DisplayDevice.javapublic final void setProjectionLocked(SurfaceControl.Transaction t, int orientation,Rect layerStackRect, Rect displayRect) {
...t.setDisplayProjection(mDisplayToken,orientation, layerStackRect, displayRect);}}

此时Java层的updateRotationUnchecked函数分析完。


3. sendNewConfiguration函数

从上面的updateRotation()函数中看到,除了调用updateRotationUnchecked()(即displayContent.updateRotationUnchecked()),还会调用sendNewConfiguration()。

//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void sendNewConfiguration(int displayId) {try {final boolean configUpdated = mActivityManager.updateDisplayOverrideConfiguration(null /* values */, displayId);
...}} catch (RemoteException e) {}}
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Overridepublic boolean updateDisplayOverrideConfiguration(Configuration values, int displayId) {enforceCallingPermission(CHANGE_CONFIGURATION, "updateDisplayOverrideConfiguration()");synchronized (this) {...if (values == null && mWindowManager != null) {// sentinel: fetch the current configuration from the window manager//Step 1 获取一些配置信息values = mWindowManager.computeNewConfiguration(displayId);}final long origId = Binder.clearCallingIdentity();try {if (values != null) {Settings.System.clearConfiguration(values);}//Step 2updateDisplayOverrideConfigurationLocked(values, null /* starting */,false /* deferResume */, displayId, mTmpUpdateConfigurationResult);return mTmpUpdateConfigurationResult.changes != 0;} finally {Binder.restoreCallingIdentity(origId);}}}private boolean updateDisplayOverrideConfigurationLocked(Configuration values,ActivityRecord starting, boolean deferResume, int displayId,UpdateConfigurationResult result) {...try {if (values != null) {if (displayId == DEFAULT_DISPLAY) {//Step 1:调用changes = updateGlobalConfigurationLocked(values, false /* initLocale */,false /* persistent */, UserHandle.USER_NULL /* userId */, deferResume);} else {//changes = performDisplayOverrideConfigUpdate(values, deferResume, displayId);}}//Step 2kept = ensureConfigAndVisibilityAfterUpdate(starting, changes);} finally {if (mWindowManager != null) {mWindowManager.continueSurfaceLayout();}}if (result != null) {result.changes = changes;result.activityRelaunched = !kept;}return kept;}private int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,boolean persistent, int userId, boolean deferResume) {//把Configuration数据保存在mTempConfigmTempConfig.setTo(getGlobalConfiguration());final int changes = mTempConfig.updateFrom(values);if (changes == 0) {performDisplayOverrideConfigUpdate(values, deferResume, DEFAULT_DISPLAY);return 0;}//会发送ACTION_CONFIGURATION_CHANGED广播,然后获取当前最上面活动的Activity,调用ActivityStack的ensureActivityConfigurationLocked函数和ActivityStackSupervisor的ensureActivitiesVisibleLocked函数。Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_REPLACE_PENDING| Intent.FLAG_RECEIVER_FOREGROUND| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,OP_NONE, null, false, false, MY_PID, SYSTEM_UID,UserHandle.USER_ALL);
......}private int performDisplayOverrideConfigUpdate(Configuration values, boolean deferResume,int displayId) {mTempConfig.setTo(mStackSupervisor.getDisplayOverrideConfiguration(displayId));//把Configuration数据保存在mTempConfigfinal int changes = mTempConfig.updateFrom(values);...}private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {boolean kept = true;final ActivityStack mainStack = mStackSupervisor.getFocusedStack();// mainStack is null during startup.if (mainStack != 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.starting = mainStack.topRunningActivityLocked();}//先后调用两个函数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.mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,!PRESERVE_WINDOWS);}}return kept;}

4. 应用强制设置屏幕方向

之前提过,其它途径可能会触发转屏,比如应用请求转屏。例如需要横屏的游戏(通过frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java的updateOrientationFromAppTokensLocked()方法)。

首先调用AMS的setRequestedOrientation函数,然后调用到ActivityRecord的setRequestedOrientation函数。

//frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
void setRequestedOrientation(int requestedOrientation) {final int displayId = getDisplayId();final Configuration displayConfig =mStackSupervisor.getDisplayOverrideConfiguration(displayId);//Step 1final Configuration config = mWindowContainerController.setOrientation(requestedOrientation,displayId, displayConfig, mayFreezeScreenLocked(app));if (config != null) {frozenBeforeDestroy = true;//Step 2:当返回false,就是现在的状态要改变(比如重启Activity)//然后就调用ActivityStackSupervisor的resumeTopActivitiesLocked函数来启动最上面的Activity。if (!service.updateDisplayOverrideConfigurationLocked(config, this,false /* deferResume */, displayId)) {mStackSupervisor.resumeFocusedStackTopActivityLocked();}}service.mTaskChangeNotificationController.notifyActivityRequestedOrientationChanged(task.taskId, requestedOrientation);}

其中调用到frameworks/base/services/core/java/com/android/server/wm/AppWindowContainerController.javamWindowContainerController.setOrientation函数。

//frameworks/base/services/core/java/com/android/server/wm/AppWindowContainerController.java
public Configuration setOrientation(int requestedOrientation, int displayId,Configuration displayConfig, boolean freezeScreenIfNeeded) {synchronized(mWindowMap) {if (mContainer == null) {Slog.w(TAG_WM,"Attempted to set orientation of non-existing app token: " + mToken);return null;}mContainer.setOrientation(requestedOrientation);final IBinder binder = freezeScreenIfNeeded ? mToken.asBinder() : null;//调用WMS的该函数旋转屏幕!!return mService.updateOrientationFromAppTokens(displayConfig, binder, displayId);}}

该函数调用到WMS的updateOrientationFromAppTokensLocked函数。这个函数先调用另一个updateOrientationFromAppTokensLocked函数,根据这个函数的返回值,返回true代表要旋转,就调用computeNewConfigurationLocked计算Configuration返回。

//WMS.java
private Configuration updateOrientationFromAppTokensLocked(Configuration currentConfig,IBinder freezeThisOneIfNeeded, int displayId, boolean forceUpdate) {if (!mDisplayReady) {return null;}Configuration config = null;if (updateOrientationFromAppTokensLocked(displayId, forceUpdate)) {// If we changed the orientation but mOrientationChangeComplete is already true,// we used seamless rotation, and we don't need to freeze the screen.if (freezeThisOneIfNeeded != null && !mRoot.mOrientationChangeComplete) {final AppWindowToken atoken = mRoot.getAppWindowToken(freezeThisOneIfNeeded);if (atoken != null) {atoken.startFreezingScreen();}}config = computeNewConfigurationLocked(displayId);} else if (currentConfig != null) {......}}return config;}boolean updateOrientationFromAppTokensLocked(int displayId, boolean forceUpdate) {long ident = Binder.clearCallingIdentity();try {final DisplayContent dc = mRoot.getDisplayContent(displayId);//获取上次强制设置的方向final int req = dc.getOrientation();//如果和上次设置的方向不同if (req != dc.getLastOrientation() || forceUpdate) {dc.setLastOrientation(req);//send a message to Policy indicating orientation change to take//action like disabling/enabling sensors etc.,// TODO(multi-display): Implement policy for secondary displays.if (dc.isDefaultDisplay) {mPolicy.setCurrentOrientationLw(req);}return dc.updateRotationUnchecked(forceUpdate);}return false;} finally {Binder.restoreCallingIdentity(ident);}}

5. 应用Activity强制设置方向

  1. Activity:

如果要强制设置一个Activity的横竖屏可以通过Manifest去设置,跟Activity相关的信息都会保存在ActivityInfo当中。

android:screenOrientation=["unspecified" | "user" | "behind" |"landscape" | "portrait" |"reverseLandscape" | "reversePortrait" |"sensorLandscape" | "sensorPortrait" |"sensor" | "fullSensor" | "nosensor"]
  1. Window

如果是要强制设置一个Window的横竖屏可以通过LayoutParams.screenOrientation来设置。在通过WindowManager.addView的时候把对应的LayoutParams传递给WMS。

WindowManager.LayoutParams.screenOrientation
http://www.dtcms.com/a/586382.html

相关文章:

  • 简述电子商务网站的建站流程佛山app开发公司
  • 精准突破 0.5mm 透明玻璃测量瓶颈 —— 泓川科技激光位移传感器的技术革新与成本优势
  • C++笔记-24-文件读写操作
  • 做网站需要会哪些计算机语言net源码的网站建设步骤
  • 做网站一般都是织梦网站 展示板
  • 设计配色推荐的网站广州深圳外贸公司
  • 怎样做建网站做淘客无锡定制网站制作公司
  • 北京网站建设 奥美通全网营销个人网页设计html代码实现
  • 网站开发多少人下载百度app到手机上
  • 网站进入沙盒期有哪些竞价网站
  • 朝阳市网站制作随州有哪些网站建设的公司
  • 雪球网 MD5_1038 逆向算法分析
  • 车辆类型特征智能识别
  • [法兰西公学院] Esterel A到Z (Gérard Berry 2018)
  • 液压矫平机:给金属板材“舒筋活血”的科学
  • 如何用图片文字做网站地图定位网站开发
  • 简述网站开发的三层架构互联网行业前景如何
  • 蛋白质核转位图像识别与分析系统
  • Javascript函数之函数的返回值?
  • 【常见的Markdown 图标语法】
  • 微网站微名片在线制作logo图片
  • 临沂住房和城乡建设局网站打不开江苏建筑工程招标信息网
  • 【Jenkins】Jenkins配置从节点 - Launch Agent
  • 网站开发程序都有什么免费的网站怎么建
  • asp.net企业网站建设建筑网片厂家
  • 另一个自己
  • 解决虚拟机CentOS7网络无连接问题
  • 建网站是自己做还是用CMS大连外经贸网站
  • 新闻通稿 | 软件产业迈入“智能重构”新纪元:自主进化、人机共生与责任挑战并存
  • 海口网站建设q.479185700惠网站开发用什么书