android system language change 流程
安卓系统语言切换后的更新过程.jpg切换语言核心过程分析
- updateLocales 分析
/**
* Requests the system to update the list of system locales.
* Note that the system looks halted for a while during the Locale migration,
* so the caller need to take care of it.
*/
public static void updateLocales(LocaleList locales) {
try {
final IActivityManager am = ActivityManager.getService();
final Configuration config = am.getConfiguration();
config.setLocales(locales);
config.userSetLocale = true;
am.updatePersistentConfiguration(config);
// Trigger the dirty bit for the Settings Provider.
BackupManager.dataChanged("com.android.providers.settings");
} catch (RemoteException e) {
// Intentionally left blank
}
}
updateLocales 会将承载语言变更信息的Locale 信息通过bindle机制传递到AMS
的updatePersistentConfiguration 中
- AMS 中关键操作分析
经过AMS 中方法的层层调用, 会发现最终走到了updateGlobalConfiguration 这个关键方法中,以下是updateGlobalConfiguration中的以下关键流程
// Make sure all resources in our process are updated right now, so that anyone who is going
// to retrieve resource values after we return will be sure to get the new ones. This is
// especially important during boot, where the first config change needs to guarantee all
// resources have that config before following boot code is executed.
mSystemThread.applyConfigurationToResources(mTempConfig);
// We need another copy of global config because we're scheduling some calls instead of
// running them in place. We need to be sure that object we send will be handled unchanged.
final Configuration configCopy = new Configuration(mTempConfig);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = configCopy;
msg.arg1 = userId;
mHandler.sendMessage(msg);
}
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 " + configCopy);
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
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
AppOpsManager.OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
UserHandle.USER_ALL);
if ((changes & ActivityInfo.CONFIG_LOCALE) != 0) {
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
if (initLocale || !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, SYSTEM_UID,
UserHandle.USER_ALL);
}
// Override configuration of the default display duplicates global config, so we need to
// update it also. This will also notify WindowManager about changes.
performDisplayOverrideConfigUpdate(mStackSupervisor.getConfiguration(), deferResume,
DEFAULT_DISPLAY);
return changes;
一. mSystemThread.applyConfigurationToResources(mTempConfig)和Settings.System.hasInterestingConfigurationChanges(changes))这两部是用来更新语言配置参说,也就是在此时应用在拿去resource 资源时,拿到的是对应国际化中的资源信息
二. app.thread.scheduleConfigurationChanged(configCopy);这部是拿到前台应用进程和后台应用进程, 通知对应的应用更新节界面
- 界面刷新,显示国际化信息
AMS 最终调用ActivityThread 中的scheduleConfigurationChanged, 经过一系列流转,最终走到activity中我们熟悉的onConfigurationChanged
不废话上核心
/**
* Decides whether to update an Activity's configuration and whether to inform it.
* @param activity The activity to notify of configuration change.
* @param newConfig The new configuration.
* @param amOverrideConfig The override config that differentiates the Activity's configuration
* from the base global configuration. This is supplied by
* ActivityManager.
* @param displayId Id of the display where activity currently resides.
* @param movedToDifferentDisplay Indicates if the activity was moved to different display.
* @return Configuration sent to client, null if no changes and not moved to different display.
*/
private Configuration performActivityConfigurationChanged(Activity activity,
Configuration newConfig, Configuration amOverrideConfig, int displayId,
boolean movedToDifferentDisplay) {
if (activity == null) {
throw new IllegalArgumentException("No activity provided.");
}
final IBinder activityToken = activity.getActivityToken();
if (activityToken == null) {
throw new IllegalArgumentException("Activity token not set. Is the activity attached?");
}
boolean shouldChangeConfig = false;
if (activity.mCurrentConfig == null) {
shouldChangeConfig = true;
} else {
// If the new config is the same as the config this Activity is already running with and
// the override config also didn't change, then don't bother calling
// onConfigurationChanged.
final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
amOverrideConfig)) {
// Always send the task-level config changes. For system-level configuration, 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 (!mUpdatingSystemConfig
|| (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
|| !REPORT_TO_ACTIVITY) {
shouldChangeConfig = true;
}
}
}
if (!shouldChangeConfig && !movedToDifferentDisplay) {
// Nothing significant, don't proceed with updating and reporting.
return null;
}
// Propagate the configuration change to ResourcesManager and Activity.
// ContextThemeWrappers may override the configuration for that context. We must check and
// apply any overrides defined.
Configuration contextThemeWrapperOverrideConfig = activity.getOverrideConfiguration();
// We only update an Activity's configuration if this is not a global configuration change.
// This must also be done before the callback, or else we violate the contract that the new
// resources are available in ComponentCallbacks2#onConfigurationChanged(Configuration).
// Also apply the ContextThemeWrapper override if necessary.
// NOTE: Make sure the configurations are not modified, as they are treated as immutable in
// many places.
final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
amOverrideConfig, contextThemeWrapperOverrideConfig);
mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig,
displayId, movedToDifferentDisplay);
activity.mConfigChangeFlags = 0;
activity.mCurrentConfig = new Configuration(newConfig);
// Apply the ContextThemeWrapper override if necessary.
// NOTE: Make sure the configurations are not modified, as they are treated as immutable
// in many places.
final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig,
contextThemeWrapperOverrideConfig);
if (!REPORT_TO_ACTIVITY) {
// Not configured to report to activity.
return configToReport;
}
if (movedToDifferentDisplay) {
activity.dispatchMovedToDisplay(displayId, configToReport);
}
if (shouldChangeConfig) {
activity.mCalled = false;
activity.onConfigurationChanged(configToReport);
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
" did not call through to super.onConfigurationChanged()");
}
}
return configToReport;
}
ActivityThread 方法 performActivityConfigurationChanged 中让人眼前一亮的 mResourcesManager.updateResourcesForActivity 和activity.dispatchMovedToDisplay(displayId, configToReport) ,这里更新了Activity 的资源和界面, 这只是Activity 资源的更新,那么挂载的fragment 和 window 怎么更新的呢,这里调用了activity.onConfigurationChanged(configToReport),看看里边做了什么
public void onConfigurationChanged(Configuration newConfig) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onConfigurationChanged " + this + ": " + newConfig);
mCalled = true;
mFragments.dispatchConfigurationChanged(newConfig);
if (mWindow != null) {
// Pass the configuration changed event to the window
mWindow.onConfigurationChanged(newConfig);
}
if (mActionBar != null) {
// Do this last; the action bar will need to access
// view changes from above.
mActionBar.onConfigurationChanged(newConfig);
}
}
哈哈, 在Activity onConfigurationChanged 更新了Fragment , Window , ActionBar 界面, 可以快乐的玩耍了
网友评论