Android6.0开发中屏幕旋转原理与流程分析
作者:未来薇妮她爹 发布时间:2023-06-22 19:21:23
本文实例讲述了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程序设计有所帮助。
来源:http://blog.csdn.net/xyyjxa/article/details/53939739


猜你喜欢
- 下面一段代码给大家分享了android中判断手机是否安装了qq或者微信,代码简单易懂,非常不错,具有参考借鉴价值,需要的的朋友参考下吧pub
- 如果不考虑更深层的性能问题,我个人认为ScrollerView还是很好用的。而且单用ScrollerView就可以实现分类型的Recycle
- 本文实例为大家分享了Android实现滑动效果的具体代码,供大家参考,具体内容如下坐标系与视图坐标系相辅相成1、坐标系:描述了View在屏幕
- 再看文章之前,希望大家先打开自己的微信点到朋友圈中去,仔细观察是不是发现朋友圈里的有个“九宫格”的图片区域,点击图片又会跳到图片的详细查看页
- 获取自定义菜单查询返回的结果有乱码解决方法:string Posturl = "https://api.weixin.qq.com
- 很多时候,我们需要展示在客户端展示图片,而且是动态显示,即不停地自行切换图片。下面我们来看一下具体的实现方法。首先,我们需要在XML...&
- /// <summary>/// 生成二维码/// </summary>/// <param name=&qu
- 作者: juky_huang 事件的简单解释: 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如
- 概述在JAVA JDK8 List分组的实现和用法一文中介绍了JDK 8如何对list进行分组,但是没有提到如何在分组后,获取每个分组的第一
- 本文以实例形式简单讲述了引用类型转换的几种常见方式,如:子类转换成父类,父类转换成子类,以及不是子父级关系类之间的转换。现分述如下,供大家参
- 本文实例为大家分享了Android实现淘宝秒杀的具体代码,供大家参考,具体内容如下目录结构效果图:imageViewHolderpublic
- 在上一篇里已经向大家介绍了如何使用GDI+绘制简单的图像,这一片继续向大家介绍其它一些绘图知识.1.首先我们来看下上一片中我们使用过的Pen
- 介绍Java中的享元模式(Flyweight Pattern)是一种结构型设计模式,旨在通过共享尽可能多的对象来减少内存占用和提高性能.Ja
- 一、介绍JUnit是一款优秀的开源Java单元测试框架,也是目前使用率最高最流行的测试框架,开发工具Eclipse和IDEA对JUnit都有
- IDEA 2020 源生是不支持中文的,感谢捷克工程师(可能是由国人实现)对我大天朝程序员的“照顾”,且不说这个必要性到底有多大,但从侧面体
- 一.服务层package com.demo.websocket;import java.io.IOException;import java
- 1.MyBatisX插件在使用mybatis或者mybatis-plus时,我们可以安装IDEA的MyBatis的插件 - MyBatisX
- 三层架构将整个业务应用划分为:(1)界面UI层(2)业务逻辑层(3)数据访问层对于复杂的系统分层可以让结构更加清晰,模块更加独立,便于维护。
- 本文实例为大家分享了TabLayout结合ViewPager实现页面切换效果的具体代码,供大家参考,具体内容如下先看看效果,如图:1.因为T
- 介绍Java中介者模式(Mediator Pattern)是一种行为设计模式,它可以降低多个对象之间的耦合性,通过一个中介者对象来协调这些对