Android6.0开发中屏幕旋转原理与流程分析
本文实例讲述了Android6.0开发中屏幕旋转原理与流程。分享给大家供大家参考,具体如下:
从Android系统开发开始,这里写下Android6.0屏幕旋转系统分析。
第一部分
Kenel
Android系统屏幕旋转得以实现,是靠从底层驱动gsensor中获取数据,从而判断屏幕方向的。kernelsensor的驱动先就不在这里赘述,简单介绍下,gsensor驱动注册input事件在/dev/input/下,可以通过adbgetevent-p可以查看系统所有的输入事件。
gsensor提供X/Y/Z三个方向的加速度数据,一旦注册到系统,hardware层打开设备之后,sensor就开始上报数据。注意这里很关键,sensor驱动加载完成之后,并不会立即激活,需要hardware层打开设备激活设备,设备才开始工作。
第二部分
Hardware
在hardware层,通过注册android标准modules之后,设备就打开激活,在Android系统就注册了
{.name=“Gravitysensor”, .vendor=“TheAndroidOpenSourceProject”, .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数据;
//Configurerotationlock. intuserRotation=Settings.System.getIntForUser(resolver, Settings.System.USER_ROTATION,Surface.ROTATION_0, UserHandle.USER_CURRENT); if(mUserRotation!=userRotation){ mUserRotation=userRotation; updateRotation=true; } intuserRotationMode=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的监听
voidupdateOrientationListenerLp(){ if(!mOrientationListener.canDetectOrientation()){ //Ifsensoristurnedoffornonexistentforsomereason return; } //Couldhavebeeninvokedduetoscreenturningonoroffor //changeofthecurrentlyvisiblewindow'sorientation. if(localLOGV)Slog.v(TAG,"mScreenOnEarly="+mScreenOnEarly +",mAwake="+mAwake+",mCurrentAppOrientation="+mCurrentAppOrientation +",mOrientationSensorEnabled="+mOrientationSensorEnabled +",mKeyguardDrawComplete="+mKeyguardDrawComplete +",mWindowManagerDrawComplete="+mWindowManagerDrawComplete); booleandisable=true; //Note:Wepostponetherotatingofthescreenuntilthekeyguardaswellasthe //windowmanagerhavereportedadrawcomplete. if(mScreenOnEarly&&mAwake&& mKeyguardDrawComplete&&mWindowManagerDrawComplete){ if(needSensorRunningLp()){ disable=false; //enablelistenerifnotalreadyenabled if(!mOrientationSensorEnabled){ mOrientationListener.enable(); if(localLOGV)Slog.v(TAG,"Enablinglisteners"); mOrientationSensorEnabled=true; } } } //checkifsensorsneedtobedisabled if(disable&&mOrientationSensorEnabled){ mOrientationListener.disable(); if(localLOGV)Slog.v(TAG,"Disablinglisteners"); mOrientationSensorEnabled=false; } } /** *EnablestheWindowOrientationListenersoitwillmonitorthesensorandcall *{@link#onProposedRotationChanged(int)}whenthedeviceorientationchanges. */ publicvoidenable(){ synchronized(mLock){ if(mSensor==null){ Slog.w(TAG,"Cannotdetectsensors.Notenabled"); return; } if(mEnabled==false){ if(LOG){ Slog.d(TAG,"WindowOrientationListenerenabled"); } mOrientationJudge.resetLocked(); mSensorManager.registerListener(mOrientationJudge,mSensor,mRate,mHandler); mEnabled=true; } } }
mOrientationJudge类型为OrientationJudge,其中onSensorChanged方法提供了通过gsensor各个方向的加速度数据计算方向的方法。一旦计算出屏幕方向发送变化则调用onProposedRotationChanged接口通知前面的Listener。而onProposedRotationChanged是一个抽象方法,由子类实现也PhoneWindowManger中的MyOrientationListener类
@Override publicvoidonProposedRotationChanged(introtation){ if(localLOGV)Slog.v(TAG,"onProposedRotationChanged,rotation="+rotation); mHandler.post(mUpdateRotationRunnable); } privatefinalRunnablemUpdateRotationRunnable=newRunnable(){ @Override publicvoidrun(){ //sendinteractionhinttoimproveredrawperformance mPowerManagerInternal.powerHint(PowerManagerInternal.POWER_HINT_INTERACTION,0); updateRotation(false); } }; voidupdateRotation(booleanalwaysSendConfiguration){ try{ //setorientationonWindowManager mWindowManager.updateRotation(alwaysSendConfiguration,false); }catch(RemoteExceptione){ //Ignore } }
调用windowManagerService中的updateRotation方法
@Override publicvoidupdateRotation(booleanalwaysSendConfiguration,booleanforceRelayout){ updateRotationUnchecked(alwaysSendConfiguration,forceRelayout); } publicvoidupdateRotationUnchecked(booleanalwaysSendConfiguration,booleanforceRelayout){ if(DEBUG_ORIENTATION)Slog.v(TAG,"updateRotationUnchecked(" +"alwaysSendConfiguration="+alwaysSendConfiguration+")"); longorigId=Binder.clearCallingIdentity(); booleanchanged; synchronized(mWindowMap){ changed=updateRotationUncheckedLocked(false); if(!changed||forceRelayout){ getDefaultDisplayContentLocked().layoutNeeded=true; performLayoutAndPlaceSurfacesLocked(); } } if(changed||alwaysSendConfiguration){ sendNewConfiguration(); } Binder.restoreCallingIdentity(origId); } //TODO(multidisplay):Rotateanydisplay? /** *Updatesthecurrentrotation. * *Returnstrueiftherotationhasbeenchanged.InthiscaseYOU *MUSTCALLsendNewConfiguration()TOUNFREEZETHESCREEN. */ publicbooleanupdateRotationUncheckedLocked(booleaninTransaction){ if(mDeferredRotationPauseCount>0){ //Rotationupdateshavebeenpausedtemporarily.Defertheupdateuntil //updateshavebeenresumed. if(DEBUG_ORIENTATION)Slog.v(TAG,"Deferringrotation,rotationispaused."); returnfalse; } ScreenRotationAnimationscreenRotationAnimation= mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); if(screenRotationAnimation!=null&&screenRotationAnimation.isAnimating()){ //Rotationupdatescannotbeperformedwhilethepreviousrotationchange //animationisstillinprogress.Skipthisupdate.Wewilltryupdating //againaftertheanimationisfinishedandthedisplayisunfrozen. if(DEBUG_ORIENTATION)Slog.v(TAG,"Deferringrotation,animationinprogress."); returnfalse; } if(!mDisplayEnabled){ //Nopointchoosingarotationifthedisplayisnotenabled. if(DEBUG_ORIENTATION)Slog.v(TAG,"Deferringrotation,displayisnotenabled."); returnfalse; } //TODO:Implementforcedrotationchanges. //SetmAltOrientationtoindicatethattheapplicationisreceiving //anorientationthathasdifferentmetricsthanitexpected. //eg.PortraitinsteadofLandscape. introtation=mPolicy.rotationForOrientationLw(mForcedAppOrientation,mRotation); booleanaltOrientation=!mPolicy.rotationHasCompatibleMetricsLw( mForcedAppOrientation,rotation); if(DEBUG_ORIENTATION){ Slog.v(TAG,"Applicationrequestedorientation" +mForcedAppOrientation+",gotrotation"+rotation +"whichhas"+(altOrientation?"incompatible":"compatible") +"metrics"); } if(mRotateOnBoot){ mRotation=Surface.ROTATION_0; rotation=Surface.ROTATION_90; } /*displayportrait,forceandroidrotationaccordingto90*/ if("true".equals(SystemProperties.get("persist.display.portrait","false"))){ rotation=Surface.ROTATION_90; } /*displayportraitend*/ //if("vr".equals(SystemProperties.get("ro.target.product","tablet"))) //rotation=Surface.ROTATION_0; if(mRotation==rotation&&mAltOrientation==altOrientation){ //Nochange. returnfalse; } resetWindowState(); if(DEBUG_ORIENTATION){ Slog.v(TAG, "Rotationchangedto"+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; finalDisplayContentdisplayContent=getDefaultDisplayContentLocked(); displayContent.layoutNeeded=true; finalint[]anim=newint[2]; if(displayContent.isDimming()){ anim[0]=anim[1]=0; }else{ mPolicy.selectRotationAnimationLw(anim); } startFreezingDisplayLocked(inTransaction,anim[0],anim[1]); //startFreezingDisplayLockedcanresettheScreenRotationAnimation. screenRotationAnimation= mAnimator.getScreenRotationAnimationLocked(Display.DEFAULT_DISPLAY); booleanisDelay=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{ IBindersurfaceFlinger=ServiceManager.getService("SurfaceFlinger"); if(surfaceFlinger!=null){ Slog.i(TAG,"*******TELLINGSURFACEFLINGERWEAREBOOTED!!!!!"); Parceldata=Parcel.obtain(); data.writeInterfaceToken("android.ui.ISurfaceComposer"); surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, data,null,0); data.recycle(); } }catch(RemoteExceptionex){ Slog.e(TAG,"Bootcompleted:SurfaceFlingerisdead!"); } } //Weneedtoupdateourscreensizeinformationtomatchthenewrotation.Iftherotation //hasactuallychangedthenthismethodwillreturntrueand,accordingtothecommentat //thetopofthemethod,thecallerisobligatedtocallcomputeNewConfigurationLocked(). //ByupdatingtheDisplayinfohereitwillbeavailableto //computeScreenConfigurationLockedlater. updateDisplayAndOrientationLocked(); finalDisplayInfodisplayInfo=displayContent.getDisplayInfo(); if(!inTransaction){ if(SHOW_TRANSACTIONS){ Slog.i(TAG,">>>OPENTRANSACTIONsetRotationUnchecked"); } SurfaceControl.openTransaction(); } try{ //NOTE:Wedisabletherotationintheemulatorbecause //itdoesn'tsupporthardwareOpenGLemulationyet. 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,"<<=0;i--){ WindowStatew=windows.get(i); if(w.mHasSurface){ if(DEBUG_ORIENTATION)Slog.v(TAG,"SetmOrientationChangingof"+w); w.mOrientationChanging=true; mInnerFields.mOrientationChangeComplete=false; } w.mLastFreezeDuration=0; } for(inti=mRotationWatchers.size()-1;i>=0;i--){ try{ mRotationWatchers.get(i).watcher.onRotationChanged(rotation); }catch(RemoteExceptione){ } } //TODO(multidisplay):Magnificationissupportedonlyforthedefaultdisplay. //Announcerotationonlyifwewillnotanimateaswealreadyhavethe //windowsinfinalstate.Otherwise,wemakethiscallattherotation`这里写代码片`end. if(screenRotationAnimation==null&&mAccessibilityController!=null &&displayContent.getDisplayId()==Display.DEFAULT_DISPLAY){ mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation); } returntrue; }
附:Android动态禁用或开启屏幕旋转的方法
packagecom.gwtsz.gts2.util; importandroid.content.Context; importandroid.provider.Settings; importandroid.provider.Settings.SettingNotFoundException; /** *重力感应器开关 *围绕手机屏幕旋转的设置功能编写的方法 *@authorWilson */ publicclassSensorUtil{ /** *打开重力感应,即设置屏幕可旋转 *@paramcontext */ publicstaticvoidopenSensor(Contextcontext){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION,1); } /** *关闭重力感应,即设置屏幕不可旋转 *@paramcontext */ publicstaticvoidcloseSensor(Contextcontext){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION,0); } /** *获取屏幕旋转功能开启状态 *@paramcontext *@return */ publicstaticintgetSensorState(Contextcontext){ intsensorState=0; try{ sensorState=Settings.System.getInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION); returnsensorState; }catch(SettingNotFoundExceptione){ e.printStackTrace(); } returnsensorState; } /** *判断屏幕旋转功能是否开启 */ publicstaticbooleanisOpenSensor(Contextcontext){ booleanisOpen=false; if(getSensorState(context)==1){ isOpen=true; }elseif(getSensorState(context)==0){ isOpen=false; } returnisOpen; } }
更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android视图View技巧总结》、《Android编程之activity操作技巧总结》、《Android文件操作技巧汇总》、《Android资源操作技巧汇总》及《Android控件用法总结》
希望本文所述对大家Android程序设计有所帮助。