Android6.0开发中屏幕旋转原理与流程分析

Haile ·
更新时间:2024-09-20
· 818 次阅读

本文实例讲述了Android6.0开发中屏幕旋转原理与流程。分享给大家供大家参考,具体如下:

从Android 系统开发开始,这里写下Android 6.0 屏幕旋转系统分析。

第一部分

Kenel

Android 系统屏幕旋转得以实现,是靠从底层驱动gsensor 中获取数据,从而判断屏幕方向的。kernel sensor的驱动先就不在这里赘述,简单介绍下,gsensor 驱动注册input 事件 在/dev/input/下,可以通过adb getevent -p 可以查看系统所有的输入事件。
gsensor 提供X/Y/Z 三个方向的加速度数据,一旦注册到系统,hardware 层打开设备之后,sensor 就开始上报数据。注意这里很关键,sensor 驱动加载完成之后,并不会立即激活,需要hardware 层打开设备激活设备,设备才开始工作。

第二部分

Hardware

在hardware层,通过注册android 标准modules之后,设备就打开激活,在Android 系统就注册了

{ .name = “Gravity sensor”, .vendor = “The Android Open Source Project”, .version = 1, .handle = SENSORS_HANDLE_BASE+ID_A, .type = SENSOR_TYPE_ACCELEROMETER, .maxRange = 4.0f*9.81f, .resolution = (4.0f*9.81f)/256.0f, .power = 0.2f, .minDelay = 5000, .reserved = {} },

第三部分

framework

PhoneWindownManager.java中的updateSettings()中读取系统中屏幕的设置方式,一旦开启自动旋转就调用updateOrientationListenerLp()开启读取sensor 数据;

// Configure rotation lock. int userRotation = Settings.System.getIntForUser(resolver, Settings.System.USER_ROTATION, Surface.ROTATION_0, UserHandle.USER_CURRENT); if (mUserRotation != userRotation) { mUserRotation = userRotation; updateRotation = true; } 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(); }

updateOrientationListenerLp中调用mOrientationListener.enable();调用到WindowOrientationListener.java中enable 注册gsensor的监听

void updateOrientationListenerLp() { if (!mOrientationListener.canDetectOrientation()) { // If sensor is turned off or nonexistent for some reason return; } // Could have been invoked due to screen turning on or off or // change of the currently visible window's orientation. if (localLOGV) Slog.v(TAG, "mScreenOnEarly=" + mScreenOnEarly + ", mAwake=" + mAwake + ", mCurrentAppOrientation=" + mCurrentAppOrientation + ", mOrientationSensorEnabled=" + mOrientationSensorEnabled + ", mKeyguardDrawComplete=" + mKeyguardDrawComplete + ", mWindowManagerDrawComplete=" + mWindowManagerDrawComplete); boolean disable = true; // Note: We postpone the rotating of the screen until the keyguard as well as the // window manager have reported a draw complete. if (mScreenOnEarly && mAwake && mKeyguardDrawComplete && mWindowManagerDrawComplete) { if (needSensorRunningLp()) { disable = false; //enable listener if not already enabled if (!mOrientationSensorEnabled) { mOrientationListener.enable(); if(localLOGV) Slog.v(TAG, "Enabling listeners"); mOrientationSensorEnabled = true; } } } //check if sensors need to be disabled if (disable && mOrientationSensorEnabled) { mOrientationListener.disable(); if(localLOGV) Slog.v(TAG, "Disabling listeners"); mOrientationSensorEnabled = false; } } /** * Enables the WindowOrientationListener so it will monitor the sensor and call * {@link #onProposedRotationChanged(int)} when the device orientation changes. */ public void enable() { synchronized (mLock) { if (mSensor == null) { Slog.w(TAG, "Cannot detect sensors. Not enabled"); return; } if (mEnabled == false) { if (LOG) { Slog.d(TAG, "WindowOrientationListener enabled"); } mOrientationJudge.resetLocked(); mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler); mEnabled = true; } } }

mOrientationJudge 类型为OrientationJudge ,其中onSensorChanged方法提供了通过gsensor 各个方向的加速度数据计算方向的方法。一旦计算出屏幕方向发送变化则调用onProposedRotationChanged接口通知前面的Listener。而onProposedRotationChanged是一个抽象方法,由子类实现也PhoneWindowManger 中的MyOrientationListener类

@Override public void onProposedRotationChanged(int rotation) { if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); mHandler.post(mUpdateRotationRunnable); } private final Runnable mUpdateRotationRunnable = new Runnable() { @Override public void run() { // send interaction hint to improve redraw performance mPowerManagerInternal.powerHint(PowerManagerInternal.POWER_HINT_INTERACTION, 0); updateRotation(false); } }; void updateRotation(boolean alwaysSendConfiguration) { try { //set orientation on WindowManager mWindowManager.updateRotation(alwaysSendConfiguration, false); } catch (RemoteException e) { // Ignore } }

调用windowManagerService中的updateRotation方法

@Override public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); } public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { if(DEBUG_ORIENTATION) Slog.v(TAG, "updateRotationUnchecked(" + "alwaysSendConfiguration=" + alwaysSendConfiguration + ")"); 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); } // TODO(multidisplay): Rotate any display? /** * Updates the current rotation. * * Returns true if the rotation has been changed. In this case YOU * MUST CALL sendNewConfiguration() TO UNFREEZE THE SCREEN. */ public boolean updateRotationUncheckedLocked(boolean inTransaction) { if (mDeferredRotationPauseCount > 0) { // Rotation updates have been paused temporarily. Defer the update until // updates have been resumed. if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, rotation is paused."); return false; } ScreenRotationAnimation screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); if (screenRotationAnimation != null && screenRotationAnimation.isAnimating()) { // Rotation updates cannot be performed while the previous rotation change // animation is still in progress. Skip this update. We will try updating // again after the animation is finished and the display is unfrozen. if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, animation in progress."); return false; } if (!mDisplayEnabled) { // No point choosing a rotation if the display is not enabled. if (DEBUG_ORIENTATION) Slog.v(TAG, "Deferring rotation, display is not enabled."); return false; } // TODO: Implement forced rotation changes. // Set mAltOrientation to indicate that the application is receiving // an orientation that has different metrics than it expected. // eg. Portrait instead of Landscape. int rotation = mPolicy.rotationForOrientationLw(mForcedAppOrientation, mRotation); boolean altOrientation = !mPolicy.rotationHasCompatibleMetricsLw( mForcedAppOrientation, rotation); if (DEBUG_ORIENTATION) { Slog.v(TAG, "Application requested orientation " + mForcedAppOrientation + ", got rotation " + rotation + " which has " + (altOrientation ? "incompatible" : "compatible") + " metrics"); } if (mRotateOnBoot) { mRotation = Surface.ROTATION_0; rotation = Surface.ROTATION_90; } /* display portrait, force android rotation according to 90 */ if("true".equals(SystemProperties.get("persist.display.portrait","false"))){ rotation = Surface.ROTATION_90; } /* display portrait end */ // if("vr".equals(SystemProperties.get("ro.target.product","tablet"))) // rotation = Surface.ROTATION_0; if (mRotation == rotation && mAltOrientation == altOrientation) { // No change. return false; } resetWindowState(); if (DEBUG_ORIENTATION) { Slog.v(TAG, "Rotation changed to " + rotation + (altOrientation ? " (alt)" : "") + " from " + mRotation + (mAltOrientation ? " (alt)" : "") + ", forceApp=" + mForcedAppOrientation); } mRotation = rotation; mAltOrientation = altOrientation; mPolicy.setRotationLw(mRotation); ThumbModeHelper.getInstance().setRotation(mRotation); mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_ACTIVE; mH.removeMessages(H.WINDOW_FREEZE_TIMEOUT); if (mFirstRotate) { mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, 5000); mFirstRotate = false; } else { mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION); } mWaitingForConfig = true; final DisplayContent displayContent = getDefaultDisplayContentLocked(); displayContent.layoutNeeded = true; final int[] anim = new int[2]; if (displayContent.isDimming()) { anim[0] = anim[1] = 0; } else { mPolicy.selectRotationAnimationLw(anim); } startFreezingDisplayLocked(inTransaction, anim[0], anim[1]); // startFreezingDisplayLocked can reset the ScreenRotationAnimation. screenRotationAnimation = mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); boolean isDelay = true; /*(("true".equals(SystemProperties.get("ro.config.low_ram", "false"))) ||("true".equals(SystemProperties.get("ro.mem_optimise.enable", "false")))) && (!"true".equals(SystemProperties.get("sys.cts_gts.status", "false")));*/ if (mRotateOnBoot) { try { IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger"); if (surfaceFlinger != null) { Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED !!!!!"); Parcel data = Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0); data.recycle(); } } catch (RemoteException ex) { Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!"); } } // We need to update our screen size information to match the new rotation. If the rotation // has actually changed then this method will return true and, according to the comment at // the top of the method, the caller is obligated to call computeNewConfigurationLocked(). // By updating the Display info here it will be available to // computeScreenConfigurationLocked later. updateDisplayAndOrientationLocked(); final DisplayInfo displayInfo = displayContent.getDisplayInfo(); if (!inTransaction) { if (SHOW_TRANSACTIONS) { Slog.i(TAG, ">>> OPEN TRANSACTION setRotationUnchecked"); } SurfaceControl.openTransaction(); } try { // NOTE: We disable the rotation in the emulator because // it doesn't support hardware OpenGL emulation yet. if (CUSTOM_SCREEN_ROTATION && screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) { if (screenRotationAnimation.setRotationInTransaction( rotation, mFxSession, MAX_ANIMATION_DURATION, getTransitionAnimationScaleLocked(), displayInfo.logicalWidth, displayInfo.logicalHeight)) { scheduleAnimationLocked(); } } mDisplayManagerInternal.performTraversalInTransactionFromWindowManager(); } finally { if (!inTransaction) { SurfaceControl.closeTransaction(); if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG, "<<< CLOSE TRANSACTION setRotationUnchecked"); } } } final WindowList windows = displayContent.getWindowList(); for (int i = windows.size() - 1; i >= 0; i--) { WindowState w = windows.get(i); if (w.mHasSurface) { if (DEBUG_ORIENTATION) Slog.v(TAG, "Set mOrientationChanging of " + w); w.mOrientationChanging = true; mInnerFields.mOrientationChangeComplete = false; } w.mLastFreezeDuration = 0; } for (int i=mRotationWatchers.size()-1; i>=0; i--) { try { mRotationWatchers.get(i).watcher.onRotationChanged(rotation); } catch (RemoteException e) { } } //TODO (multidisplay): Magnification is supported only for the default display. // Announce rotation only if we will not animate as we already have the // windows in final state. Otherwise, we make this call at the rotation`这里写代码片` end. if (screenRotationAnimation == null && mAccessibilityController != null && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) { mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); } return true; }

附:Android动态禁用或开启屏幕旋转的方法

package com.gwtsz.gts2.util; import android.content.Context; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; /** * 重力感应器开关 * 围绕手机屏幕旋转的设置功能编写的方法 * @author Wilson */ public class SensorUtil { /** * 打开重力感应,即设置屏幕可旋转 * @param context */ public static void openSensor(Context context){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION, 1); } /** * 关闭重力感应,即设置屏幕不可旋转 * @param context */ public static void closeSensor(Context context){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION, 0); } /** * 获取屏幕旋转功能开启状态 * @param context * @return */ public static int getSensorState(Context context){ int sensorState = 0; try { sensorState = Settings.System.getInt(context.getContentResolver(), Settings.System.ACCELEROMETER_ROTATION); return sensorState; } catch (SettingNotFoundException e) { e.printStackTrace(); } return sensorState; } /** * 判断屏幕旋转功能是否开启 */ public static boolean isOpenSensor(Context context){ boolean isOpen = false; if(getSensorState(context) == 1){ isOpen = true; }else if(getSensorState(context) == 0){ isOpen = false; } return isOpen; } }

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android文件操作技巧汇总》、《Android资源操作技巧汇总》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

您可能感兴趣的文章:Android异步方法以同步方式实现Android多线程之同步锁的使用Android seekbar(自定义)控制音量同步更新android-获取网络时间、获取特定时区时间、时间同步的方法android中ListView数据刷新时的同步方法Android实现歌曲播放时歌词同步显示具体思路Android获取点击屏幕的位置坐标Android 5.0+ 屏幕录制实现的示例代码Android自适应不同屏幕大小的全部方法Android手机屏幕同步工具asm.jar



屏幕旋转 Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号