Android 14 WMS-Configuration 与ConfigurationContainer类

Configuration的产生

Android具有较好的兼容性

Android 支持不同的分辨率,不同的尺寸,不同的sim卡,不同的地区,不同的方向,不同的模式(夜间模式,白天模式)等,

由于不同手机的尺寸大小,屏幕分辨率可能存在差异。当系统的分辨率改变时,或者windowmode改变时,app需要重新加载资源

当这些参数发生改变时,app是如何感知到这个变化的,感知到变化后又做了哪些操作;

系统是如何管理这些配置的,如何在配置改变时即使通知给相应的容器。

Android通过Configuration来统一管理这些手机的配置信息,因此当手机的Configuration的任一属性改变时,会有一个统一的流程,来通知到所有含有Configuraton的对象。

Configuration概述

Android Configuration ( 手机配置信息 ) 是用来描述手机设备的配置信息的,比如屏幕方向, 触摸屏的触摸方式等,所有的窗口和activity都含有Configuraton。

configuration的成员变量如下:

Configuration.java - OpenGrok cross reference for /frameworks/base/core/java/android/content/res/Configuration.java

属性Manifest作用
mccmccSIM卡相关(移动国家码)
mncmncSIM卡相关(移动网络码)

userSetLocale
-修改地区

screenLayout
screenLayout随着locale变化
touchscreentouchscreen触屏类型
keyboardkeyboard键盘类型变化(外接键盘)
keyboardHiddenkeyboardHidde键盘是否显示的状态变化
navigationnavigation导航按钮的类型(滚动球、方向键)
navigationHiddennavigation导航按钮是否显示
screenLayoutscreenLayout布局方向
fontScalefontScale字体缩放
uiMode-车载模式,底座模式,夜间模式
orientationorientation屏幕方向

smallestScreenWidthDp
-应用程序在正常操作中能看到的最小屏幕尺寸,选择资源文件时需要用到
densityDpiN屏幕密度

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<WindowProcessController> 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      }

Configuration更新流程 

3.1 通知app更新configuration和加载资源

3.1.1 回调到onConfigurationchanged流程

首先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<ComponentCallbacks2> 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<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts) {
5968          ArrayList<ComponentCallbacks2> callbacks
5969                  = new ArrayList<ComponentCallbacks2>();
5970  

 
5971          synchronized (mResourcesManager) {
5972              final int NAPP = mAllApplications.size();
//拿到所有的应用
5973              for (int i=0; i<NAPP; i++) {
5974                  callbacks.add(mAllApplications.get(i));
5975              }
5976              if (includeUiContexts) {
5977                  for (int i = mActivities.size() - 1; 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<NSVC; i++) {
5986                  final Service service = mServices.valueAt(i);
5987                  // If {@code includeUiContext} is set to false, WindowProviderService should not be
5988                  // collected because WindowProviderService is a UI Context.
//拿到所有的services
5989                  if (includeUiContexts || !(service instanceof WindowProviderService)) {
5990                      callbacks.add(service);
5991                  }
5992              }
5993          }
5994          synchronized (mProviderMap) {
5995              final int NPRV = mLocalProviders.size();
//拿到所有的content Provider
5996              for (int i=0; i<NPRV; i++) {
5997                  callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);
5998              }
5999          }
6000  
6001          return callbacks;
6002      }


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

3.1.2 资源重新加载

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      }

3.2 更新所有容器的configuraion

首先调用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的关系

3.2.1 Configuration的容器ConfigurationContainer

ConfigurationContainer 中重要的成员变量如下:

private Configuration mRequestedOverrideConfiguration = new Configuration();
private Configuration mResolvedOverrideConfiguration = new Configuration();
private Configuration mFullConfiguration = new Configuration();
private Configuration mMergedOverrideConfiguration = new Configuration();
private ArrayList<ConfigurationContainerListener> mChangeListeners = new ArrayList<>();

mRequestedOverrideConfiguration :

Container需要更新自己的Configuration时,请求设置的Configuration

mResolvedOverrideConfiguration:

可以通过resolveOverrideConfiguration()重新设置mResolvedOverrideConfiguration为自定义值

然后通过自定义值和mRequestedOverrideConfiguration一起作用到mFullConfiguration

mFullConfiguration:最终状态

mMergedOverrideConfiguration :

Configuration是具备继承效应的,由顶层Container传递下来的,即container改变后,子类将继承改变

3.2.2 WindowContainer家族

窗口的类图

由类图很容易得到窗口的属性结构如下:

由上述树形结构可以得到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      }

3.3:configuration change发生后的处理

在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已经被清空, 那么就会装载新的资源。对于未启动的应用则会在启动时加载新的资源。

  • 29
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值