Android具有较好的兼容性
Android 支持不同的分辨率,不同的尺寸,不同的sim卡,不同的地区,不同的方向,不同的模式(夜间模式,白天模式)等,
由于不同手机的尺寸大小,屏幕分辨率可能存在差异。当系统的分辨率改变时,或者windowmode改变时,app需要重新加载资源
当这些参数发生改变时,app是如何感知到这个变化的,感知到变化后又做了哪些操作;
系统是如何管理这些配置的,如何在配置改变时即使通知给相应的容器。
Android通过Configuration来统一管理这些手机的配置信息,因此当手机的Configuration的任一属性改变时,会有一个统一的流程,来通知到所有含有Configuraton的对象。
Android Configuration ( 手机配置信息 ) 是用来描述手机设备的配置信息的,比如屏幕方向, 触摸屏的触摸方式等,所有的窗口和activity都含有Configuraton。
configuration的成员变量如下:
Configuration.java - OpenGrok cross reference for /frameworks/base/core/java/android/content/res/Configuration.java
属性 | Manifest | 作用 |
mcc | mcc | SIM卡相关(移动国家码) |
mnc | mnc | SIM卡相关(移动网络码) |
userSetLocale |
- | 修改地区 |
screenLayout |
screenLayout | 随着locale变化 |
touchscreen | touchscreen | 触屏类型 |
keyboard | keyboard | 键盘类型变化(外接键盘) |
keyboardHidden | keyboardHidde | 键盘是否显示的状态变化 |
navigation | navigation | 导航按钮的类型(滚动球、方向键) |
navigationHidden | navigation | 导航按钮是否显示 |
screenLayout | screenLayout | 布局方向 |
fontScale | fontScale | 字体缩放 |
uiMode | - | 车载模式,底座模式,夜间模式 |
orientation | orientation | 屏幕方向 |
smallestScreenWidthDp |
- | 应用程序在正常操作中能看到的最小屏幕尺寸,选择资源文件时需要用到 |
densityDpi | N | 屏幕密度 |
Configuration还有一个成员变量:
public final WindowConfiguration windowConfiguration = new WindowConfiguration();
WindowConfiguration类中包含窗口的基本信息,如bounds信息,窗口的模式,activity的类型等
public final WindowConfiguration windowConfiguration = new WindowConfiguration();
WindowConfiguration.java
// bounds信息,包含insets,窗口相对于整个屏幕的可用区域
private final Rect mBounds = new Rect();
// app可以通过配置max_aspect改变,保存mBounds中不包含insets(系统装饰)的区域
private Rect mAppBounds;
// 最大边界,一般为屏幕大小
private final Rect mMaxBounds = new Rect();
// mDisplayRotation 转向
private int mDisplayRotation = ROTATION_UNDEFINED;
//屏幕转向
private int mRotation = ROTATION_UNDEFINED;
// 窗口的模式
private @WindowingMode int mWindowingMode;
//display的窗口模式
private @WindowingMode int mDisplayWindowingMode;
// activity类型
private @ActivityType int mActivityType;
configuration变化时 会有以下三个步骤
1:通知app调用,
2.1.1:调用所有callback 执行onConfigurationchanged,
2.1.2:调用native函数mAssets.setConfiguration更新资源
2:调用ConfigurationContainer.onRequestedOverrideConfigurationChanged更新所有容器的configuraion
3:遍历所有activityRecord 是否relaunch
6144 boolean updateDisplayOverrideConfigurationLocked() {
6145 // Preemptively cancel the running recents animation -- SysUI can't currently handle this
6146 // case properly since the signals it receives all happen post-change
6147 final RecentsAnimationController recentsAnimationController =
6148 mWmService.getRecentsAnimationController();
6149 if (recentsAnimationController != null) {
6150 recentsAnimationController.cancelAnimationForDisplayChange();
6151 }
6152
6153 Configuration values = new Configuration();
6154 computeScreenConfiguration(values);
6155
6156 mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
6157 ActivityManagerInternal::updateOomLevelsForDisplay, mAtmService.mAmInternal,
6158 mDisplayId));
6159
6160 Settings.System.clearConfiguration(values);
6161 updateDisplayOverrideConfigurationLocked(values, null /* starting */,
6162 false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
6163 return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
6164 }
4472 int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
4473 boolean persistent, int userId) {
4474
4475 mTempConfig.setTo(getGlobalConfiguration());
4476 final int changes = mTempConfig.updateFrom(values);
4477 if (changes == 0) {
4478 return 0;
4479 }
4480
4481 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateGlobalConfiguration");
4482 ProtoLog.i(WM_DEBUG_CONFIGURATION, "Updating global configuration "
4483 + "to: %s", values);
4484 writeConfigurationChanged(changes);
4485 FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_CONFIGURATION_CHANGED,
4486 values.colorMode,
4487 values.densityDpi,
4488 values.fontScale,
4489 values.hardKeyboardHidden,
4490 values.keyboard,
4491 values.keyboardHidden,
4492 values.mcc,
4493 values.mnc,
4494 values.navigation,
4495 values.navigationHidden,
4496 values.orientation,
4497 values.screenHeightDp,
4498 values.screenLayout,
4499 values.screenWidthDp,
4500 values.smallestScreenWidthDp,
4501 values.touchscreen,
4502 values.uiMode);
4503
4504 // Note: certain tests currently run as platform_app which is not allowed
4505 // to set debug system properties. To ensure that system properties are set
4506 // only when allowed, we check the current UID.
4507 if (Process.myUid() == Process.SYSTEM_UID) {
4508 if (values.mcc != 0) {
4509 SystemProperties.set("debug.tracing.mcc", Integer.toString(values.mcc));
4510 }
4511 if (values.mnc != 0) {
4512 SystemProperties.set("debug.tracing.mnc", Integer.toString(values.mnc));
4513 }
4514 }
4515
4516 if (!initLocale && !values.getLocales().isEmpty() && values.userSetLocale) {
4517 final LocaleList locales = values.getLocales();
4518 int bestLocaleIndex = 0;
4519 if (locales.size() > 1) {
4520 if (mSupportedSystemLocales == null) {
4521 mSupportedSystemLocales = Resources.getSystem().getAssets().getLocales();
4522 }
4523 bestLocaleIndex = Math.max(0, locales.getFirstMatchIndex(mSupportedSystemLocales));
4524 }
4525 SystemProperties.set("persist.sys.locale",
4526 locales.get(bestLocaleIndex).toLanguageTag());
4527 LocaleList.setDefault(locales, bestLocaleIndex);
4528 }
4529
4530 mTempConfig.seq = increaseConfigurationSeqLocked();
4531
4532 Slog.i(TAG, "Config changes=" + Integer.toHexString(changes) + " " + mTempConfig);
4533 // TODO(multi-display): Update UsageEvents#Event to include displayId.
4534 mUsageStatsInternal.reportConfigurationChange(mTempConfig, mAmInternal.getCurrentUserId());
4535
4536 // TODO: If our config changes, should we auto dismiss any currently showing dialogs?
4537 updateShouldShowDialogsLocked(mTempConfig);
4538
4539 AttributeCache ac = AttributeCache.instance();
4540 if (ac != null) {
4541 ac.updateConfiguration(mTempConfig);
4542 }
4543
4544 // Make sure all resources in our process are updated right now, so that anyone who is going
4545 // to retrieve resource values after we return will be sure to get the new ones. This is
4546 // especially important during boot, where the first config change needs to guarantee all
4547 // resources have that config before following boot code is executed.
4548 mSystemThread.applyConfigurationToResources(mTempConfig);
4549
4550 if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
4551 final Message msg = PooledLambda.obtainMessage(
4552 ActivityTaskManagerService::sendPutConfigurationForUserMsg,
4553 this, userId, new Configuration(mTempConfig));
4554 mH.sendMessage(msg);
4555 }
4556
4557 SparseArray pidMap = mProcessMap.getPidMap();
4558 for (int i = pidMap.size() - 1; i >= 0; i--) {
4559 final int pid = pidMap.keyAt(i);
4560 final WindowProcessController app = pidMap.get(pid);
4561 ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
4562 + "config %s", app.mName, mTempConfig);
4563 app.onConfigurationChanged(mTempConfig);
4564 }
4565
4566 final Message msg = PooledLambda.obtainMessage(
4567 ActivityManagerInternal::broadcastGlobalConfigurationChanged,
4568 mAmInternal, changes, initLocale);
4569 mH.sendMessage(msg);
4570
4571 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RootConfigChange");
4572 // Update stored global config and notify everyone about the change.
4573 mRootWindowContainer.onConfigurationChanged(mTempConfig);
4574 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
4575
4576 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
4577 return changes;
4578 }
首先ATMS调用WindowPrecessController.onConfigurationbinder
继而调用到ActivityThread的handerConfigurationed
进而调用到activity.onConfigurationchanged
当Configuration的操作执行完后,实现了ComponentCallbacks2接口的组件如Activity、Services、Application等将会执行回调onConfigurationChanged()方法(接口回调),从而实现正在运行的app中所有组件对Config的更新响应。 该方法collectComponentCallbacks针对同一进程下Activity的状态进行甄别,将符合条件的Act放入list以方便后面操作.。
frameworks/base/core/java/android/app/ConfigurationController.java
/**
* Update the configuration to latest.
* @param config The new configuration.
* @param compat The new compatibility information.
*/
146 void handleConfigurationChanged(@Nullable Configuration config,
147 @Nullable CompatibilityInfo compat) {
// 省略部分代码
// 遍历所有的ComponentCallbacks2
206 final ArrayList callbacks =
207 mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
208
209 freeTextLayoutCachesIfNeeded(configDiff);
210
211 if (callbacks != null) {
212 final int size = callbacks.size();
213 for (int i = 0; i < size; i++) {
214 ComponentCallbacks2 cb = callbacks.get(i);
215 if (!equivalent) {
216 performConfigurationChanged(cb, config);
217 }
218 }
219 }
frameworks/base/core/java/android/app/ActivityThread.java
5966 @Override
5967 public ArrayList collectComponentCallbacks(boolean includeUiContexts) {
5968 ArrayList callbacks
5969 = new ArrayList();
5970
5971 synchronized (mResourcesManager) {
5972 final int NAPP = mAllApplications.size();
//拿到所有的应用
5973 for (int i=0; i= 0; i--) {
5978 final Activity a = mActivities.valueAt(i).activity;
5979 if (a != null && !a.mFinished) {
//拿到所有的activity,且不是finished的状态
5980 callbacks.add(a);
5981 }
5982 }
5983 }
5984 final int NSVC = mServices.size();
5985 for (int i=0; i
227 void performConfigurationChanged(@NonNull ComponentCallbacks2 cb,
228 @NonNull Configuration newConfig) {
229 // ContextThemeWrappers may override the configuration for that context. We must check and
230 // apply any overrides defined.
231 Configuration contextThemeWrapperOverrideConfig = null;
232 if (cb instanceof ContextThemeWrapper) {
233 final ContextThemeWrapper contextThemeWrapper = (ContextThemeWrapper) cb;
234 contextThemeWrapperOverrideConfig = contextThemeWrapper.getOverrideConfiguration();
235 }
236
237 // Apply the ContextThemeWrapper override if necessary.
238 // NOTE: Make sure the configurations are not modified, as they are treated as immutable
239 // in many places.
240 final Configuration configToReport = createNewConfigAndUpdateIfNotNull(
241 newConfig, contextThemeWrapperOverrideConfig);
242 cb.onConfigurationChanged(configToReport);
// 更新到所有实现ComponentCallbacks2的组件
243 }
activity 是在oncreate 时注册回调,Service、Application、Provider同样实现了ComponentCallbacks接口,从而实现四大组件全部更新状态和资源
其中一种注册方法如下:
或者直接调用ComponentCallbacksController.registerCallbacks
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: ComponentCallbacksController registerCallbacks callbacks:com.tencent.matrix.memory.canary.trim.c$b@6b9eb9
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: java.lang.RuntimeException: jinyanmeionConfigurationChanged1
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at android.content.ComponentCallbacksController.registerCallbacks(ComponentCallbacksController.java:57)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at android.app.Application.registerComponentCallbacks(Application.java:295)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.matrix.memory.canary.b.start(SourceFile:2142)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.mm.matrix.b.a(SourceFile:154)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.mm.feature.ch.k.onCreate(SourceFile:38)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.mm.support.feature_service.e.notifyOnCreate(SourceFile:169)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.mm.support.feature_service.h.a(SourceFile:356)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at com.tencent.mm.support.feature_service.h$7.run(SourceFile:524)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1412)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:294)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1021)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1657)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1595)
12-14 14:25:40.692 8025 8087 D jinyanmeionConfigurationChanged1: at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
12-14 14:25:40.701 2071 2542 D jinyanmeionConfigurationChanged1: scheduleBroadcastsLocked sendMessage(H.BROADCAST_INTENT_MSG
configuraion发生变化的事件到达应用进程的ActivityThread后,调到ResourcesManager的applyConfigurationToResourcesLocked方法。
然后,ResourcesManager也会一个一个地通知这些缓存的Resources的,具体就是调用Resource类的updateConfiguration方法
首先是根据参数config和metrics来更新设备的当前配置信息(屏幕大小和密码、国家、地区、语言、键盘情况等)
接着再调用成员变量mAssets所指向的一个java层的AssetManager对象的成员函数setConfiguration来将这些配置信息设置到与之关联的C++层的AssetManager对象中,这样一来,我们在通知Resource获取资源时,Native层就会根据这个配置信息寻找到合适的资源返回,从而达到多屏幕适配的效果。
然后调用AssetManager的setConfiguration方法,通过native方法调用缓存资源成员变量的onConfigurationChange函数清除缓存中不合适当前Configuration的资源
加载到正确的资源
onConfigurationChange的参数configChanges标记了configuration的改变位,是通过前面调用calcConfigChanges得到的,这个函数会先计算出新的Configuration和旧的Configuration的改变位change, 然后调用ActivityInfo.activityInfoConfigToNative将change转换为native的Cofiguration改变位。因为native的改变位和ActivityInfo定义的位不一样。所以需要转换一下,才能匹配后面资源对应的位,检查资源是否会受到configuration的影响。 mDrawableCache.onConfigurationChange,就是调用ThemedResourceCache的onConfigurationChange函数,这个函数直接调用prune(int configChanges)函数:
这个函数主要是 遍历每一个资源,查看该资源是否已经过期,过期的话需要重新获取。
371 public void updateConfiguration(Configuration config, DisplayMetrics metrics,
372 CompatibilityInfo compat) {
373 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
374 try {
375 synchronized (mAccessLock) {
376 if (DEBUG_CONFIG) {
377 Slog.i(TAG, "**** Updating config of " + this + ": old config is "
378 + mConfiguration + " old compat is "
379 + mDisplayAdjustments.getCompatibilityInfo());
380 Slog.i(TAG, "**** Updating config of " + this + ": new config is "
381 + config + " new compat is " + compat);
382 }
383 if (compat != null) {
384 mDisplayAdjustments.setCompatibilityInfo(compat);
385 }
386 if (metrics != null) {
387 mMetrics.setTo(metrics);
388 }
389 // NOTE: We should re-arrange this code to create a Display
390 // with the CompatibilityInfo that is used everywhere we deal
391 // with the display in relation to this app, rather than
392 // doing the conversion here. This impl should be okay because
393 // we make sure to return a compatible display in the places
394 // where there are public APIs to retrieve the display... but
395 // it would be cleaner and more maintainable to just be
396 // consistently dealing with a compatible display everywhere in
397 // the framework.
398 mDisplayAdjustments.getCompatibilityInfo().applyToDisplayMetrics(mMetrics);
399
400 final @Config int configChanges = calcConfigChanges(config);
401
402 // If even after the update there are no Locales set, grab the default locales.
403 LocaleList locales = mConfiguration.getLocales();
404 if (locales.isEmpty()) {
405 locales = LocaleList.getDefault();
406 mConfiguration.setLocales(locales);
407 }
408
409 if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
410 if (locales.size() > 1) {
411 // The LocaleList has changed. We must query the AssetManager's available
412 // Locales and figure out the best matching Locale in the new LocaleList.
413 String[] availableLocales = mAssets.getNonSystemLocales();
414 if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
415 // No app defined locales, so grab the system locales.
416 availableLocales = mAssets.getLocales();
417 if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
418 availableLocales = null;
419 }
420 }
421
422 if (availableLocales != null) {
423 final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
424 availableLocales);
425 if (bestLocale != null && bestLocale != locales.get(0)) {
426 mConfiguration.setLocales(new LocaleList(bestLocale, locales));
427 }
428 }
429 }
430 }
431
432 if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
433 mMetrics.densityDpi = mConfiguration.densityDpi;
434 mMetrics.density =
435 mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
436 }
437
438 // Protect against an unset fontScale.
439 mMetrics.scaledDensity = mMetrics.density *
440 (mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);
441 mMetrics.fontScaleConverter =
442 FontScaleConverterFactory.forScale(mConfiguration.fontScale);
443
444 final int width, height;
445 if (mMetrics.widthPixels >= mMetrics.heightPixels) {
446 width = mMetrics.widthPixels;
447 height = mMetrics.heightPixels;
448 } else {
449 //noinspection SuspiciousNameCombination
450 width = mMetrics.heightPixels;
451 //noinspection SuspiciousNameCombination
452 height = mMetrics.widthPixels;
453 }
454
455 final int keyboardHidden;
456 if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
457 && mConfiguration.hardKeyboardHidden
458 == Configuration.HARDKEYBOARDHIDDEN_YES) {
459 keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
460 } else {
461 keyboardHidden = mConfiguration.keyboardHidden;
462 }
463
464 mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
465 adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
466 mConfiguration.orientation,
467 mConfiguration.touchscreen,
468 mConfiguration.densityDpi, mConfiguration.keyboard,
469 keyboardHidden, mConfiguration.navigation, width, height,
470 mConfiguration.smallestScreenWidthDp,
471 mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
472 mConfiguration.screenLayout, mConfiguration.uiMode,
473 mConfiguration.colorMode, mConfiguration.getGrammaticalGender(),
474 Build.VERSION.RESOURCES_SDK_INT);
475
476 if (DEBUG_CONFIG) {
477 Slog.i(TAG, "**** Updating config of " + this + ": final config is "
478 + mConfiguration + " final compat is "
479 + mDisplayAdjustments.getCompatibilityInfo());
480 }
481
482 mDrawableCache.onConfigurationChange(configChanges);
483 mColorDrawableCache.onConfigurationChange(configChanges);
484 mComplexColorCache.onConfigurationChange(configChanges);
485 mAnimatorCache.onConfigurationChange(configChanges);
486 mStateListAnimatorCache.onConfigurationChange(configChanges);
487
488 flushLayoutCache();
489 }
490 synchronized (sSync) {
491 if (mPluralRule != null) {
492 mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
493 }
494 }
495 } finally {
496 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
497 }
498 }
首先调用mRootWindowContainer的onConfigurationChanged()
int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
boolean persistent, int userId) {
//省略部分代码
// Update stored global config and notify everyone about the change.
mRootWindowContainer.onConfigurationChanged(mTempConfig);
return changes;
}
mRootWindowContainer继承于ConfigurationContainer,ConfigurationContainer是窗口的基本容器,因此有必要讲一下ConfigurationContainer与Configuration的关系
ConfigurationContainer 中重要的成员变量如下:
private Configuration mRequestedOverrideConfiguration = new Configuration();
private Configuration mResolvedOverrideConfiguration = new Configuration();
private Configuration mFullConfiguration = new Configuration();
private Configuration mMergedOverrideConfiguration = new Configuration();
private ArrayList mChangeListeners = new ArrayList<>();
mRequestedOverrideConfiguration :
Container需要更新自己的Configuration时,请求设置的Configuration
mResolvedOverrideConfiguration:
可以通过resolveOverrideConfiguration()重新设置mResolvedOverrideConfiguration为自定义值
然后通过自定义值和mRequestedOverrideConfiguration一起作用到mFullConfiguration
mFullConfiguration:最终状态
mMergedOverrideConfiguration :
Configuration是具备继承效应的,由顶层Container传递下来的,即container改变后,子类将继承改变
窗口的类图
由上述树形结构可以得到mRootWindowContainer.onConfigurationChanged(mTempConfig)开始遍历的原因:
RootWindowContainer是树形结构的根,采用先遍历右子树,再遍历左子树最后遍历到根节点的方法,更新到所有的wm中
Configuration更新时:
先调用onRequestedOverrideConfigurationChanged()方法将新的overrideConfiguration暂存到mRequestedOverrideConfiguration中,
然后调用自己的onConfigurationChanged()(所有的非根ConfigurationContainer,都会调用其父类ConfigurationContainer的onConfigurationChanged),这个方法中:
1,先通过resolveOverrideConfiguration()方法(子类可以重写)更新mResolvedOverrideConfiguration,默认通过mRequestedOverrideConfiguration跟新,重写后通过重写方法更新
2,再通过mResolvedOverrideConfiguration更新FullConfiguration
3, 再通过mResolvedOverrideConfiguration更新mMergedOverrideConfigurationm
然后对所有的孩子进行更新
更新完成后通过binder跨进程传递给其他线程
如下为代码中的更新过程
xref: /frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
2713 @Override
2714 public void onConfigurationChanged(Configuration newParentConfig) {
2715 final int lastOrientation = getConfiguration().orientation;
2716 super.onConfigurationChanged(newParentConfig);
DisplayArea.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java
599 @Override
600 public void onConfigurationChanged(Configuration newParentConfig) {
601 mTransitionController.collectForDisplayAreaChange(this);
602 mTmpConfiguration.setTo(getConfiguration());
603 super.onConfigurationChanged(newParentConfig);
604
605 if (mOrganizer != null && getConfiguration().diff(mTmpConfiguration) != 0) {
606 mOrganizerController.onDisplayAreaInfoChanged(mOrganizer, this);
607 }
608 }
WindowContainer.java - OpenGrok cross reference for /frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
508 @Override
509 public void onConfigurationChanged(Configuration newParentConfig) {
510 super.onConfigurationChanged(newParentConfig);
511 updateSurfacePositionNonOrganized();
512 scheduleAnimation();
513 if (mOverlayHost != null) {
514 mOverlayHost.dispatchConfigurationChanged(getConfiguration());
515 }
516 }
/frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java
125 public void onConfigurationChanged(Configuration newParentConfig) {
126 mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
127 resolveOverrideConfiguration(newParentConfig);
128 mFullConfiguration.setTo(newParentConfig);
129 // Do not inherit always-on-top property from parent, otherwise the always-on-top
130 // property is propagated to all children. In that case, newly added child is
131 // always being positioned at bottom (behind the always-on-top siblings).
132 mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
133 mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
134 onMergedOverrideConfigurationChanged();
135 if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
136 // This depends on the assumption that change-listeners don't do
137 // their own override resolution. This way, dependent hierarchies
138 // can stay properly synced-up with a primary hierarchy's constraints.
139 // Since the hierarchies will be merged, this whole thing will go away
140 // before the assumption will be broken.
141 // Inform listeners of the change.
142 for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
143 mChangeListeners.get(i).onRequestedOverrideConfigurationChanged(
144 mResolvedOverrideConfiguration);
145 }
146 }
147 for (int i = mChangeListeners.size() - 1; i >= 0; --i) {
148 mChangeListeners.get(i).onMergedOverrideConfigurationChanged(
149 mMergedOverrideConfiguration);
150 }
151 for (int i = getChildCount() - 1; i >= 0; --i) {
152 dispatchConfigurationToChild(getChildAt(i), mFullConfiguration);
153 }
154 }
在configuration的任何一种情况发生后,系统会重启Activity。如果在系统configuration change发生的时候不希望系统重启Activity,可以设置Activity的android:configChanges属性。如果设置android:configChanges="orientation|screenSize",那么当系统的Activity的orientation和screenSize发生变化时系统不会重启Activity,Activity的onConfigurationChanged(Configuraton newConfig)方法会被调用,方法参数为最新的配置。但是我们没有设置keyboardHidden值,当系统键盘变化时还是会重启Activity。
如果设置android:configChanges="orientation|screenSize" 属性会加载到info。
final ActivityInfo info; // activity info provided by developer in AndroidManifest
如果在manifest.xml中配置了configChnages属性则表示由app自己来处理configuration change,就会回调Activity等组件的onConfigurationChanged方法。否则就重启当前这个activity(这个重启步骤位于当activity回到前台时执行onDestroy->onStart->onResume),而重启之前,旧的resources已经被清空, 那么就会装载新的资源。对于未启动的应用则会在启动时加载新的资源。