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程序设计有所帮助。
上一篇: 中国足球