首页 > 系统 > Android > 正文

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

2019-10-22 18:22:38
字体:
来源:转载
供稿:网友

本文实例讲述了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类

@Overridepublic 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方法

@Overridepublic 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开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表