本文實(shí)例講述了Android6.0開發(fā)中屏幕旋轉(zhuǎn)原理與流程。分享給大家供大家參考,具體如下:
從Android 系統(tǒng)開發(fā)開始,這里寫下Android 6.0 屏幕旋轉(zhuǎn)系統(tǒng)分析。
第一部分
Kenel
Android 系統(tǒng)屏幕旋轉(zhuǎn)得以實(shí)現(xiàn),是靠從底層驅(qū)動(dòng)gsensor 中獲取數(shù)據(jù),從而判斷屏幕方向的。kernel sensor的驅(qū)動(dòng)先就不在這里贅述,簡(jiǎn)單介紹下,gsensor 驅(qū)動(dòng)注冊(cè)input 事件 在/dev/input/下,可以通過adb getevent -p 可以查看系統(tǒng)所有的輸入事件。
gsensor 提供X/Y/Z 三個(gè)方向的加速度數(shù)據(jù),一旦注冊(cè)到系統(tǒng),hardware 層打開設(shè)備之后,sensor 就開始上報(bào)數(shù)據(jù)。注意這里很關(guān)鍵,sensor 驅(qū)動(dòng)加載完成之后,并不會(huì)立即激活,需要hardware 層打開設(shè)備激活設(shè)備,設(shè)備才開始工作。
第二部分
Hardware
在hardware層,通過注冊(cè)android 標(biāo)準(zhǔn)modules之后,設(shè)備就打開激活,在Android 系統(tǒng)就注冊(cè)了
{ .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()中讀取系統(tǒng)中屏幕的設(shè)置方式,一旦開啟自動(dòng)旋轉(zhuǎn)就調(diào)用updateOrientationListenerLp()開啟讀取sensor 數(shù)據(jù);
// 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中調(diào)用mOrientationListener.enable();調(diào)用到WindowOrientationListener.java中enable 注冊(cè)gsensor的監(jiān)聽
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 各個(gè)方向的加速度數(shù)據(jù)計(jì)算方向的方法。一旦計(jì)算出屏幕方向發(fā)送變化則調(diào)用onProposedRotationChanged接口通知前面的Listener。而onProposedRotationChanged是一個(gè)抽象方法,由子類實(shí)現(xiàn)也PhoneWindowManger 中的MyOrientationListener類
@Overridepublic 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 }}調(diào)用windowManagerService中的updateRotation方法
@Overridepublic 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動(dòng)態(tài)禁用或開啟屏幕旋轉(zhuǎn)的方法
package com.gwtsz.gts2.util;import android.content.Context;import android.provider.Settings;import android.provider.Settings.SettingNotFoundException;/** * 重力感應(yīng)器開關(guān) * 圍繞手機(jī)屏幕旋轉(zhuǎn)的設(shè)置功能編寫的方法 * @author Wilson */public class SensorUtil { /** * 打開重力感應(yīng),即設(shè)置屏幕可旋轉(zhuǎn) * @param context */ public static void openSensor(Context context){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION, 1); } /** * 關(guān)閉重力感應(yīng),即設(shè)置屏幕不可旋轉(zhuǎn) * @param context */ public static void closeSensor(Context context){ Settings.System.putInt(context.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION, 0); } /** * 獲取屏幕旋轉(zhuǎn)功能開啟狀態(tài) * @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; } /** * 判斷屏幕旋轉(zhuǎn)功能是否開啟 */ public static boolean isOpenSensor(Context context){ boolean isOpen = false; if(getSensorState(context) == 1){ isOpen = true; }else if(getSensorState(context) == 0){ isOpen = false; } return isOpen; }}希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注