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 Studio 视频播放失败 start called in state1 异常怎么解决
本文地址:http://www.codeinn.net/misctech/12939.html