欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

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

程序员文章站 2022-05-26 22:56:04
本文实例讲述了android6.0开发中屏幕旋转原理与流程。分享给大家供大家参考,具体如下: 从android 系统开发开始,这里写下android 6.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程序设计有所帮助。