Android6.0 旋转屏幕(二)旋转设备
2016-12-16 11:13
1711 查看
上篇博客中我们一直提到updateRotationUnchecked函数,这篇博客我们就来分析下这个函数,这个函数可以说是旋转屏幕的一个核心函数,我们主要看下updateRotationUncheckedLocked和sendNewConfiguration函数,当updateRotationUncheckedLocked返回true代表设备已经旋转,这个时候要调用sendNewConfiguration函数通知AMS来使相应的Activity重新Launch等。
sendNewConfiguration我们放到下篇博客分析,这里我们先分析updateRotationUncheckedLocked函数。
PhoneWindowManager的rotationForOrientationLw函数我们就不分析了,就是获取sensor的rotation,然后计算返回我们需要的rotation。我们继续看updateRotationUncheckedLocked函数的剩下代码。
DisplayInfo updateDisplayAndOrientationLocked() {
......
// Update application display metrics.
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
synchronized(displayContent.mDisplaySizeLock) {
displayInfo.rotation = mRotation;//旋转
displayInfo.logicalWidth = dw;
displayInfo.logicalHeight = dh;
displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
displayInfo.appWidth = appWidth;
displayInfo.appHeight = appHeight;
displayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
displayInfo.getAppMetrics(mDisplayMetrics);
if (displayContent.mDisplayScalingDisabled) {
displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
} else {
displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayContent.getDisplayId(), displayInfo);setDisplayInfoOverrideFromWindowManager就是调用了setDisplayInfoOverrideFromWindowManagerInternal
setDisplayInfoOverrideFromWindowManagerInternal函数找到相应的display然后调用其setDisplayInfoOverrideFromWindowManagerLocked函数。
最后到LogicalDisplay的setDisplayInfoOverrideFromWindowManagerLocked函数中,把DisplayInfo数据放到了mOverrideDisplayInfo中
然后在updateRotationUncheckedLocked又调用了如下代码:
这样我们又到了DisplayManagerService的performTraversalInTransactionFromWindowManager函数,然后又调用了performTraversalInTransactionFromWindowManagerInternal函数,我们来看下
performTraversalInTransactionLocked函数,遍历所有的Device,先调用了configureDisplayInTransactionLocked函数,然后调用了各个Device的performTraversalInTransactionLocked,而普通的Device的为空,因此这里我们不关注这个函数。
下面我们主要看configureDisplayInTransactionLocked函数,这个函数就是找到那个LogicalDisplay 然后调用其configureDisplayInTransactionLocked函数
LogicalDisplay 的configureDisplayInTransactionLocked函数,就是设置长宽,旋转角度等。
我们来看下getDisplayInfoLocked函数就是获取mInfo的数据,而mOverrideDisplayInfo如有数据就要copy到mInfo中去。
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.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
}
}
return mInfo;
}
最后调用了DisplayDevice的setProjectionInTransactionLocked函数,然后调用了SurfaceControl的setDisplayProjection函数。
public final void setProjectionInTransactionLocked(int orientation,
Rect layerStackRect, Rect displayRect) {
if (mCurrentOrientation != orientation
|| mCurrentLayerStackRect == null
|| !mCurrentLayerStackRect.equals(layerStackRect)
|| mCurrentDisplayRect == null
|| !mCurrentDisplayRect.equals(displayRect)) {
mCurrentOrientation = orientation;
if (mCurrentLayerStackRect == null) {
mCurrentLayerStackRect = new Rect();
}
mCurrentLayerStackRect.set(layerStackRect);
if (mCurrentDisplayRect == null) {
mCurrentDisplayRect = new Rect();
}
mCurrentDisplayRect.set(displayRect);
SurfaceControl.setDisplayProjection(mDisplayToken,
orientation, layerStackRect, displayRect);
}
}SurfaceControl.setDisplayProjection函数如下,这个函数最后又调用了JNI函数,最后是根据mDisplayToken来设置到相应的设备中去。
这样updateRotationUncheckedLocked函数分析完了,当设备旋转了之后我们就要调用sendNewConfiguration,这个函数下篇博客分析。这里我们发现DisplayManagerService的相关流程还不是很熟悉(包括和SurfaceControl的联系等),后续也要分析下。
public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { long origId = Binder.clearCallingIdentity(); boolean changed; synchronized(mWindowMap) { changed = updateRotationUncheckedLocked(false); if (!changed || forceRelayout) { getDefaultDisplayContentLocked().layoutNeeded = true; performLayoutAndPlaceSurfacesLocked(); } } if (changed || alwaysSendConfiguration) { sendNewConfiguration();//后续分析 } Binder.restoreCallingIdentity(origId); }
sendNewConfiguration我们放到下篇博客分析,这里我们先分析updateRotationUncheckedLocked函数。
PhoneWindowManager的rotationForOrientationLw获取rotation
下面是部分代码,我们先调用PhoneWindowManager的rotationForOrientationLw函数来获取rotation,然后与之前的mRotation对比是否有变化,没有变化直接返回false。有变化将mRotation重新赋值。...... int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation); boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( mForcedAppOrientation, rotation); if (mRotation == rotation && mAltOrientation == altOrientation) { // No change. return false; } mRotation = rotation; mAltOrientation = altOrientation; ......
PhoneWindowManager的rotationForOrientationLw函数我们就不分析了,就是获取sensor的rotation,然后计算返回我们需要的rotation。我们继续看updateRotationUncheckedLocked函数的剩下代码。
updateDisplayAndOrientationLocked DisplayInfo数据放入display中
在updateRotationUncheckedLocked中还调用了updateDisplayAndOrientationLocked函数,我们也来看下,这个函数把各种数据更新下放到DisplayInfo中,最后调用了DisplayManagerService的setDisplayInfoOverrideFromWindowManager函数DisplayInfo updateDisplayAndOrientationLocked() {
......
// Update application display metrics.
final int appWidth = mPolicy.getNonDecorDisplayWidth(dw, dh, mRotation);
final int appHeight = mPolicy.getNonDecorDisplayHeight(dw, dh, mRotation);
final DisplayInfo displayInfo = displayContent.getDisplayInfo();
synchronized(displayContent.mDisplaySizeLock) {
displayInfo.rotation = mRotation;//旋转
displayInfo.logicalWidth = dw;
displayInfo.logicalHeight = dh;
displayInfo.logicalDensityDpi = displayContent.mBaseDisplayDensity;
displayInfo.appWidth = appWidth;
displayInfo.appHeight = appHeight;
displayInfo.getLogicalMetrics(mRealDisplayMetrics,
CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null);
displayInfo.getAppMetrics(mDisplayMetrics);
if (displayContent.mDisplayScalingDisabled) {
displayInfo.flags |= Display.FLAG_SCALING_DISABLED;
} else {
displayInfo.flags &= ~Display.FLAG_SCALING_DISABLED;
}
mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
displayContent.getDisplayId(), displayInfo);setDisplayInfoOverrideFromWindowManager就是调用了setDisplayInfoOverrideFromWindowManagerInternal
@Override public void setDisplayInfoOverrideFromWindowManager(int displayId, DisplayInfo info) { setDisplayInfoOverrideFromWindowManagerInternal(displayId, info); }
setDisplayInfoOverrideFromWindowManagerInternal函数找到相应的display然后调用其setDisplayInfoOverrideFromWindowManagerLocked函数。
private void setDisplayInfoOverrideFromWindowManagerInternal( int displayId, DisplayInfo info) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); if (display != null) { if (display.setDisplayInfoOverrideFromWindowManagerLocked(info)) { sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); scheduleTraversalLocked(false); } } } }
最后到LogicalDisplay的setDisplayInfoOverrideFromWindowManagerLocked函数中,把DisplayInfo数据放到了mOverrideDisplayInfo中
public 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); mInfo = null; return true; } } else if (mOverrideDisplayInfo != null) { mOverrideDisplayInfo = null; mInfo = null; return true; } return false; }
设置设备旋转
然后在updateRotationUncheckedLocked又调用了如下代码:mDisplayManagerInternal.performTraversalInTransactionFromWindowManager();
这样我们又到了DisplayManagerService的performTraversalInTransactionFromWindowManager函数,然后又调用了performTraversalInTransactionFromWindowManagerInternal函数,我们来看下
private void performTraversalInTransactionFromWindowManagerInternal() { synchronized (mSyncRoot) { if (!mPendingTraversal) { return; } mPendingTraversal = false; performTraversalInTransactionLocked(); } // List is self-synchronized copy-on-write. for (DisplayTransactionListener listener : mDisplayTransactionListeners) { listener.onDisplayTransaction(); } }
performTraversalInTransactionLocked函数,遍历所有的Device,先调用了configureDisplayInTransactionLocked函数,然后调用了各个Device的performTraversalInTransactionLocked,而普通的Device的为空,因此这里我们不关注这个函数。
private void performTraversalInTransactionLocked() { // Clear all viewports before configuring displays so that we can keep // track of which ones we have configured. clearViewportsLocked(); // Configure each display device. final int count = mDisplayDevices.size(); for (int i = 0; i < count; i++) { DisplayDevice device = mDisplayDevices.get(i); configureDisplayInTransactionLocked(device); device.performTraversalInTransactionLocked(); } // Tell the input system about these new viewports. if (mInputManagerInternal != null) { mHandler.sendEmptyMessage(MSG_UPDATE_VIEWPORT); } }
下面我们主要看configureDisplayInTransactionLocked函数,这个函数就是找到那个LogicalDisplay 然后调用其configureDisplayInTransactionLocked函数
private void configureDisplayInTransactionLocked(DisplayDevice device) { final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0; LogicalDisplay display = findLogicalDisplayForDeviceLocked(device); if (!ownContent) { if (display != null && !display.hasContentLocked()) { // If the display does not have any content of its own, then // automatically mirror the default logical display contents. display = null; } if (display == null) { display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); } } // Apply the logical display configuration to the display device. if (display == null) { // TODO: no logical display for the device, blank it Slog.w(TAG, "Missing logical display to use for physical display device: " + device.getDisplayDeviceInfoLocked()); return; } display.configureDisplayInTransactionLocked(device, info.state == Display.STATE_OFF);
LogicalDisplay 的configureDisplayInTransactionLocked函数,就是设置长宽,旋转角度等。
public void configureDisplayInTransactionLocked(DisplayDevice device, boolean isBlanked) { // Set the layer stack. device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack); // Set the color transform and mode. if (device == mPrimaryDisplayDevice) { device.requestColorTransformAndModeInTransactionLocked( mRequestedColorTransformId, mRequestedModeId); } else { device.requestColorTransformAndModeInTransactionLocked(0, 0); // Revert to default. } // Only grab the display info now as it may have been changed based on the requests above. final DisplayInfo displayInfo = getDisplayInfoLocked();//获取mInfo final DisplayDeviceInfo displayDeviceInfo = device.getDisplayDeviceInfoLocked();//获取设备的info // 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); // Set the orientation. // The orientation specifies how the physical coordinate system of the display // is rotated when the contents of the logical display are rendered. int orientation = Surface.ROTATION_0; if ((displayDeviceInfo.flags & DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT) != 0) { orientation = displayInfo.rotation; } // Apply the physical rotation of the display device itself. orientation = (orientation + displayDeviceInfo.rotation) % 4;//方向是根据传入的方向加上原有设备的方向除以4 // Set the frame. // The frame specifies the rotated physical coordinates into which the viewport // is mapped. We need to take care to preserve the aspect ratio of the viewport. // Currently we maximize the area to fill the display, but we could try to be // more clever and match resolutions. boolean rotated = (orientation == Surface.ROTATION_90 || orientation == Surface.ROTATION_270); int physWidth = rotated ? displayDeviceInfo.height : displayDeviceInfo.width;//旋转了长宽是否转换 int physHeight = rotated ? displayDeviceInfo.width : displayDeviceInfo.height; // Determine whether the width or height is more constrained to be scaled. // physWidth / displayInfo.logicalWidth => letter box // or physHeight / displayInfo.logicalHeight => pillar box // // We avoid a division (and possible floating point imprecision) here by // multiplying the fractions by the product of their denominators before // comparing them. int displayRectWidth, displayRectHeight; if ((displayInfo.flags & Display.FLAG_SCALING_DISABLED) != 0) { displayRectWidth = displayInfo.logicalWidth; displayRectHeight = displayInfo.logicalHeight; } else if (physWidth * displayInfo.logicalHeight < physHeight * displayInfo.logicalWidth) { // Letter box. displayRectWidth = physWidth; displayRectHeight = displayInfo.logicalHeight * physWidth / displayInfo.logicalWidth; } else { // Pillar box. displayRectWidth = displayInfo.logicalWidth * physHeight / displayInfo.logicalHeight; displayRectHeight = physHeight; } int displayRectTop = (physHeight - displayRectHeight) / 2; int displayRectLeft = (physWidth - displayRectWidth) / 2; mTempDisplayRect.set(displayRectLeft, displayRectTop, displayRectLeft + displayRectWidth, displayRectTop + displayRectHeight); mTempDisplayRect.left += mDisplayOffsetX; mTempDisplayRect.right += mDisplayOffsetX; mTempDisplayRect.top += mDisplayOffsetY; mTempDisplayRect.bottom += mDisplayOffsetY; device.setProjectionInTransactionLocked(orientation, mTempLayerStackRect, mTempDisplayRect); }
我们来看下getDisplayInfoLocked函数就是获取mInfo的数据,而mOverrideDisplayInfo如有数据就要copy到mInfo中去。
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.logicalDensityDpi = mOverrideDisplayInfo.logicalDensityDpi;
mInfo.physicalXDpi = mOverrideDisplayInfo.physicalXDpi;
mInfo.physicalYDpi = mOverrideDisplayInfo.physicalYDpi;
}
}
return mInfo;
}
最后调用了DisplayDevice的setProjectionInTransactionLocked函数,然后调用了SurfaceControl的setDisplayProjection函数。
public final void setProjectionInTransactionLocked(int orientation,
Rect layerStackRect, Rect displayRect) {
if (mCurrentOrientation != orientation
|| mCurrentLayerStackRect == null
|| !mCurrentLayerStackRect.equals(layerStackRect)
|| mCurrentDisplayRect == null
|| !mCurrentDisplayRect.equals(displayRect)) {
mCurrentOrientation = orientation;
if (mCurrentLayerStackRect == null) {
mCurrentLayerStackRect = new Rect();
}
mCurrentLayerStackRect.set(layerStackRect);
if (mCurrentDisplayRect == null) {
mCurrentDisplayRect = new Rect();
}
mCurrentDisplayRect.set(displayRect);
SurfaceControl.setDisplayProjection(mDisplayToken,
orientation, layerStackRect, displayRect);
}
}SurfaceControl.setDisplayProjection函数如下,这个函数最后又调用了JNI函数,最后是根据mDisplayToken来设置到相应的设备中去。
public static void setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } if (layerStackRect == null) { throw new IllegalArgumentException("layerStackRect must not be null"); } if (displayRect == null) { throw new IllegalArgumentException("displayRect must not be null"); } nativeSetDisplayProjection(displayToken, orientation, layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom, displayRect.left, displayRect.top, displayRect.right, displayRect.bottom); }
这样updateRotationUncheckedLocked函数分析完了,当设备旋转了之后我们就要调用sendNewConfiguration,这个函数下篇博客分析。这里我们发现DisplayManagerService的相关流程还不是很熟悉(包括和SurfaceControl的联系等),后续也要分析下。
相关文章推荐
- Android6.0开发中屏幕旋转原理与流程分析
- iOS中检测当前设备的旋转方向(关闭屏幕旋转)
- iPhone屏幕旋转锁定时判断设备的方向
- 4.0上设备屏幕旋转依赖设备的探索
- 原生JS如何监听移动设备屏幕横竖屏的旋转
- android6.0 otg连接设备 点亮屏幕(案例)
- 单击Android设备后退键,主屏幕键以及旋转屏幕如何影响Activity的生命周期
- [RK3288][Android6.0] 调试笔记 --- 屏幕显示旋转方法
- Android6.0 旋转屏幕(五)WMS启动应用流程(屏幕方向相关)
- 获取 WinCE 移动设备屏幕旋转方向
- 关于设备屏幕自动旋转一些问题
- IOS 设备屏幕旋转相关收集
- IOS设备屏幕旋转相关技术收集
- 获取 WinCE 移动设备屏幕旋转方向
- ios开发屏幕旋转锁定时判断设备的方向
- UIView设置为自动适配屏幕时,当用户旋转设备的时候,会调用layoutSubviews方法,我们只需重写 这个方法,然后判断用户屏幕的方向。在调整每个空间的位置即可。
- android 重力感应和屏幕旋转关系
- 总结iOS App开发中控制屏幕旋转的几种方式
- Android4.4系统增加LCD 屏幕旋转功能
- android Activity生命周期(设备旋转、数据恢复等)与启动模式