安卓语言切换流程
文章目录
Settings
- 在settings应用中,通过LocaleDragAndDropAdapter来实现语言列表拖拽切换语言的,在拖拽结束后会调用updateLocalesWhenAnimationStops表示当动画结束后,进行locale的更新操作 -  
| 1 | public void updateLocalesWhenAnimationStops(final LocaleList localeList) { | 
- LocalePicker.updateLocales(mLocalesToSetNext);当动画结束时,调用系统方法,更新locale,LocalePicker的位置:
android\frameworks\base\core\java\com\android\internal\app\LocalePicker.java
请求系统来更新系统的语言设置
| 1 | /** | 
- 通过am.updatePersistentConfiguration(config);来进行配置更新和持久化,am的真正实现为 ActivityManagerService.java,先确认权限,
| 1 | @Override | 
- 详细分析updateConfigurationLocked:
| 1 | /** | 
- updateGlobalConfiguration:更新默认的global配置,并通知各个监听器,比较长的代码,注释解读:该方法主要做了以下事情
| 1 | 
 | 
以上方法主要做了以下几件事情:
- 设置persist.sys.locale,持久化保存 
- 发送SEND_LOCALE_TO_MOUNT_DAEMON_MSG消息通知,storageManager 
- 调用mStackSupervisor的回调mStackSupervisor.onConfigurationChanged(mTempConfig); - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- /** 
 * Notify that parent config changed and we need to update full configuration.
 * @see #mFullConfiguration
 */
 void onConfigurationChanged(Configuration newParentConfig) {
 mFullConfiguration.setTo(newParentConfig);
 mFullConfiguration.updateFrom(mOverrideConfiguration);
 for (int i = getChildCount() - 1; i >= 0; --i) {
 final ConfigurationContainer child = getChildAt(i);
 child.onConfigurationChanged(mFullConfiguration);
 }
 }
- mUsageStatsService.reportConfigurationChange(mTempConfig,mUserController.getCurrentUserIdLocked()); 报告UsageEvents - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17- @Override 
 public void reportConfigurationChange(Configuration config, int userId) {
 if (config == null) {
 Slog.w(TAG, "Configuration event reported with a null config");
 return;
 }
 UsageEvents.Event event = new UsageEvents.Event();
 event.mPackage = "android";
 // This will later be converted to system time.
 event.mTimeStamp = SystemClock.elapsedRealtime();
 event.mEventType = UsageEvents.Event.CONFIGURATION_CHANGE;
 event.mConfiguration = new Configuration(config);
 mHandler.obtainMessage(MSG_REPORT_EVENT, userId, 0, event).sendToTarget();
 }
- mSystemThread.applyConfigurationToResources(mTempConfig); - ActivityThread.Java - 1 
 2
 3
 4
 5- public final void applyConfigurationToResources(Configuration config) { 
 synchronized (mResourcesManager) {
 mResourcesManager.applyConfigurationToResourcesLocked(config, null);
 }
 }- ResourcesManager.java - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84- public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config, 
 @Nullable CompatibilityInfo compat) {
 try {
 Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
 "ResourcesManager#applyConfigurationToResourcesLocked");
 if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
 if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
 + mResConfiguration.seq + ", newSeq=" + config.seq);
 return false;
 }
 int changes = mResConfiguration.updateFrom(config);
 // Things might have changed in display manager, so clear the cached displays.
 mAdjustedDisplays.clear();
 DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
 if (compat != null && (mResCompatibilityInfo == null ||
 !mResCompatibilityInfo.equals(compat))) {
 mResCompatibilityInfo = compat;
 changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
 | ActivityInfo.CONFIG_SCREEN_SIZE
 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
 }
 Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
 ApplicationPackageManager.configurationChanged();
 //Slog.i(TAG, "Configuration changed in " + currentPackageName());
 Configuration tmpConfig = null;
 for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
 ResourcesKey key = mResourceImpls.keyAt(i);
 WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
 ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
 if (r != null) {
 if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
 + r + " config to: " + config);
 int displayId = key.mDisplayId;
 boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
 DisplayMetrics dm = defaultDisplayMetrics;
 final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
 if (!isDefaultDisplay || hasOverrideConfiguration) {
 if (tmpConfig == null) {
 tmpConfig = new Configuration();
 }
 tmpConfig.setTo(config);
 // Get new DisplayMetrics based on the DisplayAdjustments given
 // to the ResourcesImpl. Update a copy if the CompatibilityInfo
 // changed, because the ResourcesImpl object will handle the
 // update internally.
 DisplayAdjustments daj = r.getDisplayAdjustments();
 if (compat != null) {
 daj = new DisplayAdjustments(daj);
 daj.setCompatibilityInfo(compat);
 }
 dm = getDisplayMetrics(displayId, daj);
 if (!isDefaultDisplay) {
 applyNonDefaultDisplayMetricsToConfiguration(dm, tmpConfig);
 }
 if (hasOverrideConfiguration) {
 tmpConfig.updateFrom(key.mOverrideConfiguration);
 }
 r.updateConfiguration(tmpConfig, dm, compat);
 } else {
 r.updateConfiguration(config, dm, compat);
 }
 //Slog.i(TAG, "Updated app resources " + v.getKey()
 // + " " + r + ": " + r.getConfiguration());
 } else {
 //Slog.i(TAG, "Removing old resources " + v.getKey());
 mResourceImpls.removeAt(i);
 }
 }
 return changes != 0;
 } finally {
 Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
 }
 }- Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat); 更新资源配置 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22- if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) { 
 if (locales.size() > 1) {
 // The LocaleList has changed. We must query the AssetManager's available
 // Locales and figure out the best matching Locale in the new LocaleList.
 String[] availableLocales = mAssets.getNonSystemLocales();
 if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
 // No app defined locales, so grab the system locales.
 availableLocales = mAssets.getLocales();
 if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
 availableLocales = null;
 }
 }
 if (availableLocales != null) {
 final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
 availableLocales);
 if (bestLocale != null && bestLocale != locales.get(0)) {
 mConfiguration.setLocales(new LocaleList(bestLocale, locales));
 }
 }
 }
 }- ApplicationPackageManager.configurationChanged();清空缓存 - 1 
 2
 3
 4
 5
 6- static void configurationChanged() { 
 synchronized (sSync) {
 sIconCache.clear();
 sStringCache.clear();
 }
 }
- app.thread.scheduleConfigurationChanged(configCopy);遍历每个activityThread,应用新的配置 - ActivityThread.java - 1 
 2
 3
 4- public void scheduleConfigurationChanged(Configuration config) { 
 updatePendingConfiguration(config);
 sendMessage(H.CONFIGURATION_CHANGED, config);
 }- CONFIGURATION_CHANGED: - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11- case CONFIGURATION_CHANGED: 
 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged");
 mCurDefaultDisplayDpi = ((Configuration)msg.obj).densityDpi;
 mUpdatingSystemConfig = true;
 try {
 handleConfigurationChanged((Configuration) msg.obj, null);
 } finally {
 mUpdatingSystemConfig = false;
 }
 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 break;- ActivityThread.handleConfigurationChanged - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74- final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { 
 int configDiff = 0;
 // This flag tracks whether the new configuration is fundamentally equivalent to the
 // existing configuration. This is necessary to determine whether non-activity
 // callbacks should receive notice when the only changes are related to non-public fields.
 // We do not gate calling {@link #performActivityConfigurationChanged} based on this flag
 // as that method uses the same check on the activity config override as well.
 final boolean equivalent = config != null && mConfiguration != null
 && (0 == mConfiguration.diffPublicOnly(config));
 synchronized (mResourcesManager) {
 if (mPendingConfiguration != null) {
 if (!mPendingConfiguration.isOtherSeqNewer(config)) {
 config = mPendingConfiguration;
 mCurDefaultDisplayDpi = config.densityDpi;
 updateDefaultDensity();
 }
 mPendingConfiguration = null;
 }
 if (config == null) {
 return;
 }
 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
 + config);
 mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
 updateLocaleListFromAppContext(mInitialApplication.getApplicationContext(),
 mResourcesManager.getConfiguration().getLocales());
 if (mConfiguration == null) {
 mConfiguration = new Configuration();
 }
 if (!mConfiguration.isOtherSeqNewer(config) && compat == null) {
 return;
 }
 configDiff = mConfiguration.updateFrom(config);
 config = applyCompatConfiguration(mCurDefaultDisplayDpi);
 final Theme systemTheme = getSystemContext().getTheme();
 if ((systemTheme.getChangingConfigurations() & configDiff) != 0) {
 systemTheme.rebase();
 }
 final Theme systemUiTheme = getSystemUiContext().getTheme();
 if ((systemUiTheme.getChangingConfigurations() & configDiff) != 0) {
 systemUiTheme.rebase();
 }
 }
 ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);
 freeTextLayoutCachesIfNeeded(configDiff);
 if (callbacks != null) {
 final int N = callbacks.size();
 for (int i=0; i<N; i++) {
 ComponentCallbacks2 cb = callbacks.get(i);
 if (cb instanceof Activity) {
 // If callback is an Activity - call corresponding method to consider override
 // config and avoid onConfigurationChanged if it hasn't changed.
 Activity a = (Activity) cb;
 performConfigurationChangedForActivity(mActivities.get(a.getActivityToken()),
 config);
 } else if (!equivalent) {
 performConfigurationChanged(cb, config);
 }
 }
 }
 }
| 1 | 
app.thread.scheduleConfigurationChanged(configCopy);
回到
ActivityThread.java
| 1 | public void scheduleConfigurationChanged(Configuration config) { | 
CONFIGURATION_CHANGED:
| 1 | 
 | 
ActivityThread.handleConfigurationChanged
| 1 | final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) { | 
其中调用log打印
| 1 | 01-09 03:12:27.360: I/linlian(5221): LocaleDragAndDropAdapter.updateLocalesWhenAnimationStops() |