上篇博客我们分析了WMS中的updateRotationUnchecked函数,当旋转角度有变化时会调用sendNewConfiguration函数。这篇博客我们就来分析下这个函数。
-
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);
-
}
AMS的updateConfiguration函数
这个函数最后是调用了AMS的updateConfiguration函数。
-
void sendNewConfiguration() {
-
try {
-
mActivityManager.updateConfiguration(null);
-
}
catch (RemoteException e) {
-
}
-
}
我们再来看AMS的updateConfiguration函数,这个时候参数values是空,所以会调用WMS的computeNewConfiguration来获取一些配置信息。然后会调用updateConfigurationLocked函数。
-
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();
-
}
-
-
if (mWindowManager != null) {
-
mProcessList.applyDisplaySize(mWindowManager);
-
}
-
-
final
long origId = Binder.clearCallingIdentity();
-
if (values != null) {
-
Settings.System.clearConfiguration(values);
-
}
-
updateConfigurationLocked(values, null,
false,
false);
-
Binder.restoreCallingIdentity(origId);
-
}
-
}
我们再来看updateConfigurationLocked函数,先把Configuration数据保存在mConfiguration,然后会调用ActivityThread的scheduleConfigurationChanged。紧接着会发送ACTION_CONFIGURATION_CHANGED广播,然后获取当前最上面活动的Activity,调用ActivityStack的ensureActivityConfigurationLocked函数和ActivityStackSupervisor的ensureActivitiesVisibleLocked函数。
-
boolean updateConfigurationLocked(Configuration values,
-
ActivityRecord starting, boolean persistent, boolean initLocale) {
-
int changes =
0;
-
-
if (values != null) {
-
Configuration newConfig =
new Configuration(mConfiguration);
-
changes = newConfig.updateFrom(values);
-
if (changes !=
0) {
-
if (!initLocale && values.locale != null && values.userSetLocale) {
-
final String languageTag = values.locale.toLanguageTag();
-
SystemProperties.
set(
"persist.sys.locale", languageTag);
-
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,
-
values.locale));
-
}
-
-
mConfigurationSeq++;
-
if (mConfigurationSeq <=
0) {
-
mConfigurationSeq =
1;
-
}
-
newConfig.seq = mConfigurationSeq;
-
mConfiguration = newConfig;
//保存数据
-
Slog.i(TAG,
"Config changes=" + Integer.toHexString(changes) +
" " + newConfig);
-
mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
-
//mUsageStatsService.noteStartConfig(newConfig);
-
-
final Configuration configCopy =
new Configuration(mConfiguration);
-
-
// TODO: If our config changes, should we auto dismiss any currently
-
// showing dialogs?
-
mShowDialogs = shouldShowDialogs(newConfig);
-
-
AttributeCache ac = AttributeCache.instance();
-
if (ac != null) {
-
ac.updateConfiguration(configCopy);
-
}
-
-
mSystemThread.applyConfigurationToResources(configCopy);
-
-
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
-
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
-
msg.obj =
new Configuration(configCopy);
-
mHandler.sendMessage(msg);
-
}
-
-
for (
int i=mLruProcesses.size()
-1; i>=
0; i--) {
-
ProcessRecord app = mLruProcesses.get(i);
-
try {
-
if (app.thread != null) {
-
//调用ActivityThread的scheduleConfigurationChanged函数
-
app.thread.scheduleConfigurationChanged(configCopy);
-
}
-
}
catch (Exception e) {
-
}
-
}
-
Intent intent =
new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
//发送广播
-
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-
| Intent.FLAG_RECEIVER_REPLACE_PENDING
-
| Intent.FLAG_RECEIVER_FOREGROUND);
-
broadcastIntentLocked(null, null, intent, null, null,
0, null, null,
-
null, AppOpsManager.OP_NONE, null,
false,
false,
-
MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-
if ((changes&ActivityInfo.CONFIG_LOCALE) !=
0) {
-
intent =
new Intent(Intent.ACTION_LOCALE_CHANGED);
-
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-
if (!mProcessesReady) {
-
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-
}
-
broadcastIntentLocked(null, null, intent,
-
null, null,
0, null, null, null, AppOpsManager.OP_NONE,
-
null,
false,
false, MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
-
}
-
}
-
}
-
-
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;
-
}
我们先来看看WMS的setNewConfiguration函数,主要是调用了performLayoutAndPlaceSurfacesLocked,这个函数主要是刷新系统UI和计算窗口等,我们在后续分析WMS的时候会详细介绍。这里就不分析了
-
@
Override
-
public
void
setNewConfiguration
(Configuration config) {
-
if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
-
"setNewConfiguration()")) {
-
throw
new SecurityException(
"Requires MANAGE_APP_TOKENS permission");
-
}
-
-
synchronized(mWindowMap) {
-
mCurConfiguration =
new Configuration(config);
-
if (mWaitingForConfig) {
-
mWaitingForConfig =
false;
-
mLastFinishedFreezeSource =
"new-config";
-
}
-
performLayoutAndPlaceSurfacesLocked();
-
}
-
}
ActivityThread的handleConfigurationChanged
ActivityThead最后会调用到handleConfigurationChanged函数,然后会调用collectComponentCallbacks函数来统计回调,后面就调用performConfigurationChanged函数。
-
final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
-
-
int configDiff =
0;
-
-
synchronized (mResourcesManager) {
-
if (mPendingConfiguration != null) {
-
if (!mPendingConfiguration.isOtherSeqNewer(config)) {
-
config = mPendingConfiguration;
-
mCurDefaultDisplayDpi = config.densityDpi;
-
updateDefaultDensity();
-
}
-
mPendingConfiguration = null;
-
}
-
-
if (config == null) {
-
return;
-
}
-
-
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
-
-
if (mConfiguration == null) {
-
mConfiguration =
new Configuration();
-
}
-
if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
-
return;
-
}
-
-
configDiff = mConfiguration.updateFrom(config);
-
config = applyCompatConfiguration(mCurDefaultDisplayDpi);
-
}
-
-
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);
-
}
-
}
-
}
最后在performConfigurationChanged中会调用回调的onConfigurationChanged函数。最后也会调用到应用的Activity的onConfigurationChanged函数中。
-
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 (activity != null) {
-
activity.mCalled =
false;
-
}
-
-
boolean shouldChangeConfig =
false;
-
if ((activity == null) || (activity.mCurrentConfig == null)) {
-
shouldChangeConfig =
true;
-
}
else {
-
-
// If the new config is the same as the config this Activity
-
// is already running with then don't bother calling
-
// onConfigurationChanged
-
int diff = activity.mCurrentConfig.diff(config);
-
if (diff !=
0) {
-
// If this activity doesn't handle any of the config changes
-
// then don't bother calling onConfigurationChanged as we're
-
// going to destroy it.
-
if ((~activity.mActivityInfo.getRealConfigChanged() & diff) ==
0) {
-
shouldChangeConfig =
true;
-
}
-
}
-
}
-
if (shouldChangeConfig) {
-
cb.onConfigurationChanged(config);
-
-
if (activity != null) {
-
if (!activity.mCalled) {
-
throw
new SuperNotCalledException(
-
"Activity " + activity.getLocalClassName() +
-
" did not call through to super.onConfigurationChanged()");
-
}
-
activity.mConfigChangeFlags =
0;
-
activity.mCurrentConfig =
new Configuration(config);
-
}
-
}
-
}
ActivityStack的ensureActivityConfigurationLocked函数
我们继续回到AMS的updateConfigurationLocked函数,看如下代码,先获取最上面的ActivityRecord,然后调用ActivityStack的ensureActivityConfigurationLocked函数。
-
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);
-
}
-
}
ensureActivityConfigurationLocked函数如下,先要满足一定条件(需要在AndroidManifest.xml中配置下config change相关的配置,我们可以看下之前的博客http://blog.csdn.net/kc58236582/article/details/53665682)根据当前Activity状态来调用relaunchActivityLocked(这个函数又会调用ActivityThread的scheduleRelaunchActivity函数重新启动Activity)或者destroyActivityLocked函数。随后调用ActivityThread的scheduleActivityConfigurationChanged。
-
final boolean ensureActivityConfigurationLocked(ActivityRecord r,
-
int globalChanges) {
-
......
-
-
if ((changes&(~r.info.getRealConfigChanged())) !=
0 || r.forceNewConfig) {
//从这句判断知道需要在AndroidManifest中配置的
-
// 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;
-
}
-
-
// Default case: the activity can handle this new configuration, so hand it over.
-
// NOTE: We only forward the stack override configuration as the system level configuration
-
// changes is always sent to all processes when they happen so it can just use whatever
-
// system level configuration it last got.
-
if (r.app != null && r.app.thread != null) {
-
try {
-
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Sending new config to " + r);
-
r.app.thread.scheduleActivityConfigurationChanged(
-
r.appToken,
new Configuration(mOverrideConfig));
-
}
catch (RemoteException e) {
-
// If process died, whatever.
-
}
-
}
-
r.stopFreezingScreenLocked(
false);
-
-
return
true;
-
}
这样应用就会重新启动等(完成随系统旋转)。