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

本文实例讲述了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开发 旋转屏幕导致Activity重建解决方法
  • Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案
  • 详解Android中Runtime解决屏幕旋转问题(推荐)
  • Android webview旋转屏幕导致页面重新加载问题解决办法
  • Android6.0 固定屏幕功能实现方法及实例
  • Android6.0 屏幕固定功能详解
  • 详解Android权限管理之Android 6.0运行时权限及解决办法
  • Android6.0动态申请权限所遇到的问题小结
  • Android 6.0调用相机图册崩溃的完美解决方案
  • Android适配安卓6.0蓝牙通讯实现过程
  • Android 6.0权限申请详解及权限资料整理
(0)

相关推荐

  • Android webview旋转屏幕导致页面重新加载问题解决办法

    Android webview旋转屏幕导致页面重新加载问题解决办法 1. 在create时候加个状态判断 protected void onCreate(Bundle savedInstanceState){ ... if (savedInstanceState == null) { mWebView.loadUrl("your_url"); } ... } 2. 重载保存状态的函数: @Override protected void onSaveInstanceState(Bundl

  • 详解Android中Runtime解决屏幕旋转问题(推荐)

    前言 大家或许在iOS程序开发中经常遇到屏幕旋转问题,比如说希望指定的页面进行不同的屏幕旋转,但由于系统提供的方法是导航控制器的全局方法,无法随意的达到这种需求.一般的解决方案是继承UINavrgationViewController,重写该类的相关方法,这样虽然也能解决问题,但是在重写的过程中至少产生两个多余的文件和不少的代码,这显然不是我们想要的.下面就使用一种较底层的方法解决这个问题. 基本原理 动态的改变UINavrgationViewController的全局方法,将我们自己重写的su

  • Android实现屏幕旋转方法总结

    本文实例总结了Android实现屏幕旋转方法.分享给大家供大家参考.具体如下: 在介绍之前,我们需要先了解默认情况下android屏幕旋转的机制: 默认情况下,当用户手机的重力感应器打开后,旋转屏幕方向,会导致当前activity发生onDestroy-> onCreate,这样会重新构造当前activity和界面布局,如果在Camera界面,则表现为卡顿或者黑屏一段时间.如果是在横竖屏UI设计方面,那么想很好地支持屏幕旋转,则建议在res中建立layout-land和layout-port两个

  • Android6.0 屏幕固定功能详解

    可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能. 屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换. 一.设置固定屏幕 我们先来看SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java的代码,这段代码就是长按home键出现几个Activity,然后按

  • Android 6.0调用相机图册崩溃的完美解决方案

    最近客户更新系统发现,以前的项目在调用相机的时候,闪退掉了,很奇怪,后来查阅后发现,Android 6.0以后需要程序授权相机权限,默认会给出提示,让用户授权,个人感觉这一特性很好,大概如下: 导入Android V4, V7包! Android Studio 导入很简单,右键项目后找到dependency就ok了. 继承AppCompatActivity public class MainActivity extends AppCompatActivity 引入需要的类库 import and

  • Android适配安卓6.0蓝牙通讯实现过程

    事先说明: 安卓蓝牙需要定位权限申请,在安卓6.0需要用户手动确认权限后才能使用,各位可以自行查询资料实现,如果嫌麻烦,可以用第三方Bmob集成好的工具类进行实现,详细可以看http://blog.csdn.net/qq_30379689/article/details/52223244 蓝牙连接过程: 1.查询用户是否开启蓝牙. 2.搜索附近的可用的蓝牙. 3.进行蓝牙配对. 4.进行蓝牙连接. 5.获取输入流和输出流. 6.发送消息. 晒上我自己画的美图: 实验效果图: 实现需要的权限:由于

  • Android 6.0权限申请详解及权限资料整理

    在android 6.0开始,部分的权限需要我们动态申请,也就是说当我们的打开app的时候系统不会主动像您申请app所需要的部分权限,需要客户在使用app的时候主动的去申请. 一.权限的申请两步骤: 1.权限申请: /** * @param permissions需要申请的权限 * @param requestCode申请回调code */ public static void requestPermissions(final @NonNull Activity activity,final @

  • Android6.0动态申请权限所遇到的问题小结

    白天在做SDK23版本的适配,遇到了不少坑,现在抽空记下来,以此为戒. 首先要知道哪些坑,就得先了解一些定义和基本使用方式. 那么先介绍一下动态申请的权限分组情况. 下面的权限组是由谷歌官方定义的,目的是在申请权限时,只要用户允许同一权限组的任意一条权限,那么该组的其他权限也就默认是允许的.不过据高人介绍,在使用时最好是用到哪个权限就具体的请求该权限,因为保不齐哪天谷歌一高兴就把权限组换了甚至删了 group:android.permission-group.CONTACTS permissio

  • Android6.0 固定屏幕功能实现方法及实例

    Android 固定屏幕功能 可能大家看到这个标题不知道是什么东西,我先说明下,android6.0在设置->安全->屏幕固定开启后,然后再长按home键出现最近的几个Activity可以选择一个图钉按钮就开启了屏幕固定功能. 屏幕固定开启后,屏幕只能固定在设定的Task上的Activity切换. 一.设置固定屏幕 我们先来看SystemUI/src/com/Android/systemui/recents/ScreenPinningRequest.Java的代码,这段代码就是长按home键出

  • Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

    一.概述 运行时变更就是设备在运行时发生变化(例如屏幕旋转.键盘可用性及语言).发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务,以便恢复activity的状态. 为此,google提供了三种解决方案: 对于少量数据: 通过onSaveInstanceState(),保存有关应用状态的数据. 然后在 onCreate() 或 onRestoreInstanceState()期间恢复 Activity 状态. 对于大量数据:用

  • 详解Android权限管理之Android 6.0运行时权限及解决办法

    前言: 今天还是围绕着最近面试的一个热门话题Android 6.0权限适配来总结学习,其实Android 6.0权限适配我们公司是在今年5月份才开始做,算是比较晚的吧,不过现在Android 6.0以上设备越来越多了,所以Android 6.0 权限适配是必不可少的工作,这里主要介绍一下我们公司是如何做Android 6.0权限适配的. Android 6.0以下非运行时权限: 根据上面博客我们很清楚的知道,Android的权限其实就是为了程序之间更加的安全的访问,所以权限有等级之分,比如:No

  • Android开发 旋转屏幕导致Activity重建解决方法

    Android开发文档上专门有一小节解释这个问题.简单来说,Activity是负责与用户交互的最主要机制,任何"设置"(Configuration)的改变都可能对Activity的界面造成影响,这时系统会销毁并重建Activity以便反映新的Configuration. "屏幕方向"(orientation)是一个Configuration,通过查看Configuration类的javadoc可以看到其他Configuration还有哪些:如fontScale.ke

随机推荐