Android 屏幕旋转事件流程分析
2018-03-23 10:10
393 查看
本文转自:https://blog.csdn.net/wo_sxn/article/details/52345323
WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm)
[java] view plain copy print?private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
}
在WMS中,该变量实质是PhoneWindowManager对象。PhoneWindowManager类实现了WindowManagerPolicy接口类。
PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?/** {@inheritDoc} */
@Override
public void init(Context context, IWindowManager wind
4000
owManager,
WindowManagerFuncs windowManagerFuncs) {
mContext = context;
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mOrientationListener = new MyOrientationListener(mContext, mHandler);
try {
mOrientationListener.setCurrentRotation(windowManager.getRotation());
} catch (RemoteException ex) { }
}
发现在初始化的过程中,实例化一个方位监听对象MyOrientationListener,并且给它设置了初始值。该初始值从WMS中获取,默认为0。
MyOrientationListener类是PhoneWindowManager的一个内部类,它继承了WindowOrientationListener类。
WindowOrientationListener.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?private WindowOrientationListener(Context context, Handler handler, int rate) {
mHandler = handler;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist
mSensorEventListener = new SensorEventListenerImpl(context);
}
}
在这里,默认采用的传感器是加速度传感器,USE_GRAVITY_SENSOR:false
在Android7.0中,默认采用的是方向传感器。
WindowOrientationListener构造器中,mSensorManager、mSensor、mSensorEventListener对象。在后面的enable方法中,通过mSensorManager调用registerListener为mSensor注册监听事件mSensorEventListener。
[java] view plain copy print? * Enables the WindowOrientationListener so it will monitor the sensor and call
* {@link #onProposedRotationChanged(int)} when the device orientation changes.
*/
public void enable() {
mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
mEnabled = true;
}
}
}
这里不关注这些个点,还是去看传感器监听处理这一块内容。
SensorEventListenerImpl是WindowOrientationListener的内部类,它实现了接口。
@Override
[java] view plain copy print?public void onSensorChanged(SensorEvent event) {
int proposedRotation;
int oldProposedRotation;
synchronized (mLock) {
// The vector given in the SensorEvent points straight up (towards the sky) under
// ideal conditions (the phone is not accelerating). I’ll call this up vector
// elsewhere.
float x = event.values[ACCELEROMETER_DATA_X];
float y = event.values[ACCELEROMETER_DATA_Y];
float z = event.values[ACCELEROMETER_DATA_Z];
}
}
// Tell the listener.
if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
if (LOG) {
Slog.v(TAG, ”Proposed rotation changed! proposedRotation=” + proposedRotation
+ ”, oldProposedRotation=” + oldProposedRotation);
}
onProposedRotationChanged(proposedRotation);
}
}
[java] view plain copy print?/**
* Called when the rotation view of the device has changed.
*
* This method is called whenever the orientation becomes certain of an orientation.
* It is called each time the orientation determination transitions from being
* uncertain to being certain again, even if it is the same orientation as before.
*
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see android.view.Surface
*/
public abstract void onProposedRotationChanged(int rotation);
PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?@Override
public void onProposedRotationChanged(int rotation) {
if (localLOGV) Slog.v(TAG, “onProposedRotationChanged, rotation=” + rotation);
updateRotation(false);
}
[java] view plain copy print?void updateRotation(boolean alwaysSendConfiguration) {
try {
//set orientation on WindowManager
mWindowManager.updateRotation(alwaysSendConfiguration, false); //false、false
} catch (RemoteException e) {
// Ignore
}
}
WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm)
[java] view plain copy print?/**
* Recalculate the current rotation.
*
* Called by the window manager policy whenever the state of the system changes
* such that the current rotation might need to be updated, such as when the
* device is docked or rotated into a new posture.
*/
@Override
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
[java] view plain copy print?public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
boolean changed;
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (changed || alwaysSendConfiguration) {
sendNewConfiguration();
}
}
一般情况,rotation都是发生变化的,也就是说updateRotationUncheckedLocked返回值通常为true,故会调用sendNewConfiguration。
[java] view plain copy print?/*
* Instruct the Activity Manager to fetch the current configuration and broadcast
* that to config-changed listeners if appropriate.
*/
void sendNewConfiguration() {
try {
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
}
ActivityManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
[java] view plain copy print?public void updateConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
”updateConfiguration()”);
synchronized(this) {
if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
values = mWindowManager.computeNewConfiguration();
}
updateConfigurationLocked(values, null, false, false);
}
}
[java] view plain copy print?/**
* Do either or both things: (1) change the current configuration, and (2)
* make sure the given activity is running with the (now) current
* configuration. Returns true if the activity has been left running, or
* false if <var>starting</var> is being destroyed to match the new
* configuration.
* @param persistent TODO
*/
boolean updateConfigurationLocked(Configuration values,
ctivityRecord starting, boolean persistent, boolean initLocale) {
int changes = 0;
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
for (int i=mLruProcesses.size()-1; i>=0; i–) {
ProcessRecord app = mLruProcesses.get(i);
try {
if (app.thread != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, “Sending to proc ”
+ app.processName + ” new config ” + mConfiguration);
app.thread.scheduleConfigurationChanged(configCopy);
}
} catch (Exception e) {
}
}
boolean kept = true;
final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
starting = mainStack.topRunningActivityLocked(null);
}
if (starting != null) {
kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
}
}
if (values != null && mWindowManager != null) {
mWindowManager.setNewConfiguration(mConfiguration);
}
return kept;
}
IApplicationThread是一个aidl文件。这里最终调用的是ApplicationThread对象,而ApplicationThread类是ActivityThread的一个内部类。
ActivityThread.java (android-6.0\frameworks\base\core\java\android\app)
[java] view plain copy print?public void scheduleConfigurationChanged(Configuration config) {
updatePendingConfiguration(config);
sendMessage(H.CONFIGURATION_CHANGED, config);
}
[java] view plain copy print?public void handleMessage(Message msg) {
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ”configChanged”);
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
handleConfigurationChanged((Configuration)msg.obj, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, “<<< done: ” + codeToString(msg.what));
}
[java] view plain copy print?final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
freeTextLayoutCachesIfNeeded(configDiff);
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
performConfigurationChanged(callbacks.get(i), config);
}
}
}
[java] view plain copy print?private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
// Only for Activity objects, check that they actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
// we check the runtime type and act accordingly.
Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
}
在调用完onConfigurationChanged后,Activity会重新创建。所以就回到AMS的updateConfigurationLocked方法中。
kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
mainStack:ActivityStack。
ActivityStack.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
[java] view plain copy print?/**
* Make sure the given activity matches the current configuration. Returns
* false if the activity had to be destroyed. Returns true if the
* configuration is the same, or the activity will remain running as-is
* for whatever reason. Ensures the HistoryRecord is updated with the
* correct configuration and all other bookkeeping is handled.
*/
final boolean ensureActivityConfigurationLocked(ActivityRecord r,
int globalChanges) {
if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {
// Aha, the activity isn’t handling the change, so DIE DIE DIE.
r.configChangeFlags |= changes;
r.startFreezingScreenLocked(r.app, globalChanges);
r.forceNewConfig = false;
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is destroying non-running ” + r);
destroyActivityLocked(r, true, “config”);
} else if (r.state == ActivityState.PAUSING) {
// A little annoying: we are waiting for this activity to
// finish pausing. Let’s not do anything now, but just
// flag that it needs to be restarted when done pausing.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is skipping already pausing ” + r);
r.configDestroy = true;
return true;
} else if (r.state == ActivityState.RESUMED) {
// Try to optimize this case: the configuration is changing
// and we need to restart the top, resumed activity.
// Instead of doing the normal handshaking, just say
// “restart!”.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is relaunching resumed ” + r);
relaunchActivityLocked(r, r.configChangeFlags, true);
r.configChangeFlags = 0;
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is relaunching non-resumed ” + r);
relaunchActivityLocked(r, r.configChangeFlags, false);
r.configChangeFlags = 0;
}
// All done… tell the caller we weren’t able to keep this
// activity around.
return false;
}
return true;
}
[java] view plain copy print?private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
try {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
”Moving to ” + (andResume ? “RESUMED” : “PAUSED”) + “ Relaunching ” + r);
r.forceNewConfig = false;
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
new Configuration(mOverrideConfig));
// Note: don’t need to call pauseIfSleepingLocked() here, because
// the caller will only pass in ‘andResume’ if this activity is
// currently resumed, which implies we aren’t sleeping.
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, “Relaunch failed”, e);
}
return true;
}
总结:
旋屏事件上报流程:
1、传感器(默认为加速度传感器)计算数据,决定是否上报旋屏事件。
2、上报是通过回调函数实现的,在PhoneWindowManger中实现指定接口。
3、PhoneWindowManger与WMS进行交互,通知其更新rotation。
4、WMS更新rotation后,发现的确发生改变了,去通知AMS处理。
5、AMS获取WMS中rotation数据,然后更新处理。通常流程是通过ApplicationThread与ActivityThread交互。最后调用流程是 onConfigurationChanged –> Activity重新创建。
WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm)
[java] view plain copy print?private void initPolicy() {
UiThread.getHandler().runWithScissors(new Runnable() {
@Override
public void run() {
WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper());
mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this);
}
}, 0);
}
private void initPolicy() { UiThread.getHandler().runWithScissors(new Runnable() { @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); } }, 0); }在WMS构造方法中就调用initPolicy方法。在这个方法中,很明显是在初始化mPolicy:WindowManagerPolicy这个变量。
在WMS中,该变量实质是PhoneWindowManager对象。PhoneWindowManager类实现了WindowManagerPolicy接口类。
PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?/** {@inheritDoc} */
@Override
public void init(Context context, IWindowManager wind
4000
owManager,
WindowManagerFuncs windowManagerFuncs) {
mContext = context;
mWindowManager = windowManager;
mWindowManagerFuncs = windowManagerFuncs;
mOrientationListener = new MyOrientationListener(mContext, mHandler);
try {
mOrientationListener.setCurrentRotation(windowManager.getRotation());
} catch (RemoteException ex) { }
}
/** {@inheritDoc} */ @Override public void init(Context context, IWindowManager windowManager, WindowManagerFuncs windowManagerFuncs) { mContext = context; mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; mOrientationListener = new MyOrientationListener(mContext, mHandler); try { mOrientationListener.setCurrentRotation(windowManager.getRotation()); } catch (RemoteException ex) { } }
发现在初始化的过程中,实例化一个方位监听对象MyOrientationListener,并且给它设置了初始值。该初始值从WMS中获取,默认为0。
MyOrientationListener类是PhoneWindowManager的一个内部类,它继承了WindowOrientationListener类。
WindowOrientationListener.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?private WindowOrientationListener(Context context, Handler handler, int rate) {
mHandler = handler;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist
mSensorEventListener = new SensorEventListenerImpl(context);
}
}
private WindowOrientationListener(Context context, Handler handler, int rate) { mHandler = handler; mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); mRate = rate; mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER); if (mSensor != null) { // Create listener only if sensors do exist mSensorEventListener = new SensorEventListenerImpl(context); } }看到这边,一下子就明白旋屏事件上报的大致流程。首先由传感器计算数据确认是否上报,然后通过Handler或者回调方法来处理。这么想是因为构造器中传递进来一个Handler对象,另外本身就是通过其子类调用才进入WindowOrientationListener。具体是怎么一个流程,还要分析接下来的代码。
在这里,默认采用的传感器是加速度传感器,USE_GRAVITY_SENSOR:false
在Android7.0中,默认采用的是方向传感器。
WindowOrientationListener构造器中,mSensorManager、mSensor、mSensorEventListener对象。在后面的enable方法中,通过mSensorManager调用registerListener为mSensor注册监听事件mSensorEventListener。
[java] view plain copy print? * Enables the WindowOrientationListener so it will monitor the sensor and call
* {@link #onProposedRotationChanged(int)} when the device orientation changes.
*/
public void enable() {
mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
mEnabled = true;
}
}
}
* Enables the WindowOrientationListener so it will monitor the sensor and call * {@link #onProposedRotationChanged(int)} when the device orientation changes. */ public void enable() { mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler); mEnabled = true; } } }
这里不关注这些个点,还是去看传感器监听处理这一块内容。
SensorEventListenerImpl是WindowOrientationListener的内部类,它实现了接口。
@Override
[java] view plain copy print?public void onSensorChanged(SensorEvent event) {
int proposedRotation;
int oldProposedRotation;
synchronized (mLock) {
// The vector given in the SensorEvent points straight up (towards the sky) under
// ideal conditions (the phone is not accelerating). I’ll call this up vector
// elsewhere.
float x = event.values[ACCELEROMETER_DATA_X];
float y = event.values[ACCELEROMETER_DATA_Y];
float z = event.values[ACCELEROMETER_DATA_Z];
}
}
// Tell the listener.
if (proposedRotation != oldProposedRotation && proposedRotation >= 0) {
if (LOG) {
Slog.v(TAG, ”Proposed rotation changed! proposedRotation=” + proposedRotation
+ ”, oldProposedRotation=” + oldProposedRotation);
}
onProposedRotationChanged(proposedRotation);
}
}
public void onSensorChanged(SensorEvent event) { int proposedRotation; int oldProposedRotation; synchronized (mLock) { // The vector given in the SensorEvent points straight up (towards the sky) under // ideal conditions (the phone is not accelerating). I'll call this up vector // elsewhere. float x = event.values[ACCELEROMETER_DATA_X]; float y = event.values[ACCELEROMETER_DATA_Y]; float z = event.values[ACCELEROMETER_DATA_Z]; } } // Tell the listener. if (proposedRotation != oldProposedRotation && proposedRotation >= 0) { if (LOG) { Slog.v(TAG, "Proposed rotation changed! proposedRotation=" + proposedRotation + ", oldProposedRotation=" + oldProposedRotation); } onProposedRotationChanged(proposedRotation); } }传感器数据计算过程这里省略了,在onSensorChanged方法的最后通过函数回调上报旋屏事件。回顾上面的内容,验证的确如此。
[java] view plain copy print?/**
* Called when the rotation view of the device has changed.
*
* This method is called whenever the orientation becomes certain of an orientation.
* It is called each time the orientation determination transitions from being
* uncertain to being certain again, even if it is the same orientation as before.
*
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see android.view.Surface
*/
public abstract void onProposedRotationChanged(int rotation);
/** * Called when the rotation view of the device has changed. * * This method is called whenever the orientation becomes certain of an orientation. * It is called each time the orientation determination transitions from being * uncertain to being certain again, even if it is the same orientation as before. * * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants. * @see android.view.Surface */ public abstract void onProposedRotat 170df ionChanged(int rotation);onProposedRotationChanged方法是声明在WindowOrientationListener类的一个抽象方法,它具体实现在PhoneWindowManager的一个内部类,即MyOrientationListener。
PhoneWindowManager.java (android-6.0\frameworks\base\services\core\java\com\android\server\policy)
[java] view plain copy print?@Override
public void onProposedRotationChanged(int rotation) {
if (localLOGV) Slog.v(TAG, “onProposedRotationChanged, rotation=” + rotation);
updateRotation(false);
}
@Override public void onProposedRotationChanged(int rotation) { if (localLOGV) Slog.v(TAG, "onProposedRotationChanged, rotation=" + rotation); updateRotation(false); }
[java] view plain copy print?void updateRotation(boolean alwaysSendConfiguration) {
try {
//set orientation on WindowManager
mWindowManager.updateRotation(alwaysSendConfiguration, false); //false、false
} catch (RemoteException e) {
// Ignore
}
}
void updateRotation(boolean alwaysSendConfiguration) { try { //set orientation on WindowManager mWindowManager.updateRotation(alwaysSendConfiguration, false); //false、false } catch (RemoteException e) { // Ignore } }很明显,是通知WMS更新rotation。
WindowManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\wm)
[java] view plain copy print?/**
* Recalculate the current rotation.
*
* Called by the window manager policy whenever the state of the system changes
* such that the current rotation might need to be updated, such as when the
* device is docked or rotated into a new posture.
*/
@Override
public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {
updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);
}
/** * Recalculate the current rotation. * * Called by the window manager policy whenever the state of the system changes * such that the current rotation might need to be updated, such as when the * device is docked or rotated into a new posture. */ @Override public void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) { updateRotationUnchecked(alwaysSendConfiguration, forceRelayout); }
[java] view plain copy print?public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {
boolean changed;
synchronized(mWindowMap) {
changed = updateRotationUncheckedLocked(false);
if (changed || alwaysSendConfiguration) {
sendNewConfiguration();
}
}
public void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) { boolean changed; synchronized(mWindowMap) { changed = updateRotationUncheckedLocked(false); if (changed || alwaysSendConfiguration) { sendNewConfiguration(); } }
一般情况,rotation都是发生变化的,也就是说updateRotationUncheckedLocked返回值通常为true,故会调用sendNewConfiguration。
[java] view plain copy print?/*
* Instruct the Activity Manager to fetch the current configuration and broadcast
* that to config-changed listeners if appropriate.
*/
void sendNewConfiguration() {
try {
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
}
/* * Instruct the Activity Manager to fetch the current configuration and broadcast * that to config-changed listeners if appropriate. */ void sendNewConfiguration() { try { mActivityManager.updateConfiguration(null); } catch (RemoteException e) { } }mActivityManager本质是AMS Server端,这里从WMS运行至AMS,
ActivityManagerService.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
[java] view plain copy print?public void updateConfiguration(Configuration values) {
enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,
”updateConfiguration()”);
synchronized(this) {
if (values == null && mWindowManager != null) {
// sentinel: fetch the current configuration from the window manager
values = mWindowManager.computeNewConfiguration();
}
updateConfigurationLocked(values, null, false, false);
}
}
public void updateConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); synchronized(this) { if (values == null && mWindowManager != null) { // sentinel: fetch the current configuration from the window manager values = mWindowManager.computeNewConfiguration(); } updateConfigurationLocked(values, null, false, false); } }AMS中,第一步:检查权限,没有权限则抛一个异常;第二步:从WMS中获取Configuration值;第三步:去真正更新Configuration值。
[java] view plain copy print?/**
* Do either or both things: (1) change the current configuration, and (2)
* make sure the given activity is running with the (now) current
* configuration. Returns true if the activity has been left running, or
* false if <var>starting</var> is being destroyed to match the new
* configuration.
* @param persistent TODO
*/
boolean updateConfigurationLocked(Configuration values,
ctivityRecord starting, boolean persistent, boolean initLocale) {
int changes = 0;
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
for (int i=mLruProcesses.size()-1; i>=0; i–) {
ProcessRecord app = mLruProcesses.get(i);
try {
if (app.thread != null) {
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, “Sending to proc ”
+ app.processName + ” new config ” + mConfiguration);
app.thread.scheduleConfigurationChanged(configCopy);
}
} catch (Exception e) {
}
}
boolean kept = true;
final ActivityStack mainStack = mStackSupervisor.getFocusedStack();
// mainStack is null during startup.
if (mainStack != null) {
if (changes != 0 && starting == null) {
// If the configuration changed, and the caller is not already
// in the process of starting an activity, then find the top
// activity to check if its configuration needs to change.
starting = mainStack.topRunningActivityLocked(null);
}
if (starting != null) {
kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
}
}
if (values != null && mWindowManager != null) {
mWindowManager.setNewConfiguration(mConfiguration);
}
return kept;
}
/** * Do either or both things: (1) change the current configuration, and (2) * make sure the given activity is running with the (now) current * configuration. Returns true if the activity has been left running, or * false if <var>starting</var> is being destroyed to match the new * configuration. * @param persistent TODO */ boolean updateConfigurationLocked(Configuration values, ctivityRecord starting, boolean persistent, boolean initLocale) { int changes = 0; if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); for (int i=mLruProcesses.size()-1; i>=0; i--) { ProcessRecord app = mLruProcesses.get(i); try { if (app.thread != null) { if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Sending to proc " + app.processName + " new config " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); } } catch (Exception e) { } } boolean kept = true; final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); // mainStack is null during startup. if (mainStack != null) { if (changes != 0 && starting == null) { // If the configuration changed, and the caller is not already // in the process of starting an activity, then find the top // activity to check if its configuration needs to change. starting = mainStack.topRunningActivityLocked(null); } if (starting != null) { kept = mainStack.ensureActivityConfigurationLocked(starting, changes); // And we need to make sure at this point that all other activities // are made visible with the correct configuration. mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes); } } if (values != null && mWindowManager != null) { mWindowManager.setNewConfiguration(mConfiguration); } return kept; }通常当发生横竖屏切换的时候,Activity的生命周期通常是:onConfigureationChanged –> onDestroy –> onCreate –> onStart –> onResume。一看这里就分为两步骤,一:通知Configuration已经改变;二:获取栈顶的Activity,重新运行该Activity,以适配新的Configuration。
IApplicationThread是一个aidl文件。这里最终调用的是ApplicationThread对象,而ApplicationThread类是ActivityThread的一个内部类。
ActivityThread.java (android-6.0\frameworks\base\core\java\android\app)
[java] view plain copy print?public void scheduleConfigurationChanged(Configuration config) {
updatePendingConfiguration(config);
sendMessage(H.CONFIGURATION_CHANGED, config);
}
public void scheduleConfigurationChanged(Configuration config) { updatePendingConfiguration(config); sendMessage(H.CONFIGURATION_CHANGED, config); }直接通过Handler机制与ActivityThread进行通信。
[java] view plain copy print?public void handleMessage(Message msg) {
case CONFIGURATION_CHANGED:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, ”configChanged”);
mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
handleConfigurationChanged((Configuration)msg.obj, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
if (DEBUG_MESSAGES) Slog.v(TAG, “<<< done: ” + codeToString(msg.what));
}
public void handleMessage(Message msg) { case CONFIGURATION_CHANGED: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi; handleConfigurationChanged((Configuration)msg.obj, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); }
[java] view plain copy print?final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
freeTextLayoutCachesIfNeeded(configDiff);
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
performConfigurationChanged(callbacks.get(i), config);
}
}
}
final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config); freeTextLayoutCachesIfNeeded(configDiff); if (callbacks != null) { final int N = callbacks.size(); for (int i=0; i<N; i++) { performConfigurationChanged(callbacks.get(i), config); } } }
[java] view plain copy print?private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
// Only for Activity objects, check that they actually call up to their
// superclass implementation. ComponentCallbacks2 is an interface, so
// we check the runtime type and act accordingly.
Activity activity = (cb instanceof Activity) ? (Activity) cb : null;
if (shouldChangeConfig) {
cb.onConfigurationChanged(config);
}
private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) { // Only for Activity objects, check that they actually call up to their // superclass implementation. ComponentCallbacks2 is an interface, so // we check the runtime type and act accordingly. Activity activity = (cb instanceof Activity) ? (Activity) cb : null; if (shouldChangeConfig) { cb.onConfigurationChanged(config); }很明显,在这里调用了onConfigurationChanged方法。也就是常说在横竖屏切换的时候先调用Activity的onConfigurationChanged,通常会重写这个方法,做一些保存参数之类的操作。
在调用完onConfigurationChanged后,Activity会重新创建。所以就回到AMS的updateConfigurationLocked方法中。
kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
mainStack:ActivityStack。
ActivityStack.java (android-6.0\frameworks\base\services\core\java\com\android\server\am)
[java] view plain copy print?/**
* Make sure the given activity matches the current configuration. Returns
* false if the activity had to be destroyed. Returns true if the
* configuration is the same, or the activity will remain running as-is
* for whatever reason. Ensures the HistoryRecord is updated with the
* correct configuration and all other bookkeeping is handled.
*/
final boolean ensureActivityConfigurationLocked(ActivityRecord r,
int globalChanges) {
if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) {
// Aha, the activity isn’t handling the change, so DIE DIE DIE.
r.configChangeFlags |= changes;
r.startFreezingScreenLocked(r.app, globalChanges);
r.forceNewConfig = false;
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is destroying non-running ” + r);
destroyActivityLocked(r, true, “config”);
} else if (r.state == ActivityState.PAUSING) {
// A little annoying: we are waiting for this activity to
// finish pausing. Let’s not do anything now, but just
// flag that it needs to be restarted when done pausing.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is skipping already pausing ” + r);
r.configDestroy = true;
return true;
} else if (r.state == ActivityState.RESUMED) {
// Try to optimize this case: the configuration is changing
// and we need to restart the top, resumed activity.
// Instead of doing the normal handshaking, just say
// “restart!”.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is relaunching resumed ” + r);
relaunchActivityLocked(r, r.configChangeFlags, true);
r.configChangeFlags = 0;
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
”Config is relaunching non-resumed ” + r);
relaunchActivityLocked(r, r.configChangeFlags, false);
r.configChangeFlags = 0;
}
// All done… tell the caller we weren’t able to keep this
// activity around.
return false;
}
return true;
}
/** * Make sure the given activity matches the current configuration. Returns * false if the activity had to be destroyed. Returns true if the * configuration is the same, or the activity will remain running as-is * for whatever reason. Ensures the HistoryRecord is updated with the * correct configuration and all other bookkeeping is handled. */ final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges) { if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) { // Aha, the activity isn't handling the change, so DIE DIE DIE. r.configChangeFlags |= changes; r.startFreezingScreenLocked(r.app, globalChanges); r.forceNewConfig = false; if (r.app == null || r.app.thread == null) { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is destroying non-running " + r); destroyActivityLocked(r, true, "config"); } else if (r.state == ActivityState.PAUSING) { // A little annoying: we are waiting for this activity to // finish pausing. Let's not do anything now, but just // flag that it needs to be restarted when done pausing. if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is skipping already pausing " + r); r.configDestroy = true; return true; } else if (r.state == ActivityState.RESUMED) { // Try to optimize this case: the configuration is changing // and we need to restart the top, resumed activity. // Instead of doing the normal handshaking, just say // "restart!". if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, true); r.configChangeFlags = 0; } else { if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Config is relaunching non-resumed " + r); relaunchActivityLocked(r, r.configChangeFlags, false); r.configChangeFlags = 0; } // All done... tell the caller we weren't able to keep this // activity around. return false; } return true; }
[java] view plain copy print?private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
try {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH,
”Moving to ” + (andResume ? “RESUMED” : “PAUSED”) + “ Relaunching ” + r);
r.forceNewConfig = false;
r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
!andResume, new Configuration(mService.mConfiguration),
new Configuration(mOverrideConfig));
// Note: don’t need to call pauseIfSleepingLocked() here, because
// the caller will only pass in ‘andResume’ if this activity is
// currently resumed, which implies we aren’t sleeping.
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, “Relaunch failed”, e);
}
return true;
}
private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) { try { if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Moving to " + (andResume ? "RESUMED" : "PAUSED") + " Relaunching " + r); r.forceNewConfig = false; r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes, !andResume, new Configuration(mService.mConfiguration), new Configuration(mOverrideConfig)); // Note: don't need to call pauseIfSleepingLocked() here, because // the caller will only pass in 'andResume' if this activity is // currently resumed, which implies we aren't sleeping. } catch (RemoteException e) { if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e); } return true; }一眼就看明白,同样是在ApplicationThread内部通过Handler与ActivityThead进行通信,然后真正去执行relaunch操作。
总结:
旋屏事件上报流程:
1、传感器(默认为加速度传感器)计算数据,决定是否上报旋屏事件。
2、上报是通过回调函数实现的,在PhoneWindowManger中实现指定接口。
3、PhoneWindowManger与WMS进行交互,通知其更新rotation。
4、WMS更新rotation后,发现的确发生改变了,去通知AMS处理。
5、AMS获取WMS中rotation数据,然后更新处理。通常流程是通过ApplicationThread与ActivityThread交互。最后调用流程是 onConfigurationChanged –> Activity重新创建。
相关文章推荐
- Android 7.1 屏幕旋转流程分析
- android自动屏幕旋转流程分析
- Android 7.1 WindowManagerService 屏幕旋转流程分析 (三)
- Android 6.0 屏幕旋转系统流程分析
- android自动屏幕旋转流程分析
- Android 7.1 WindowManagerService 屏幕旋转流程分析 (二)
- Android 7.1 ActivityManagerService 屏幕旋转流程分析 (四)
- android自动屏幕旋转流程分析
- Android 4.0按键事件以及电源管理流程分析
- 代码流程分析一:Settings中默认值的流程-显示-自动旋转屏幕
- android的frameworks层键盘事件处理流程分析
- Android的frameworks层键盘事件处理流程分析
- android源码分析——事件输入流程MotionEvent事件处理流程
- Android中屏幕事件触发和消费流程
- Android事件分发和View绘制流程分析(一)
- Android webkit 事件传递流程通道分析
- Android的frameworks层键盘事件处理流程分析
- 【android】点击touch事件流程分析
- android 应用监听输入法按键事件【比如搜索和回车键等】的整个流程分析
- Android输入事件流程中的EventHub分析及源码演示