属性 :
persist.sys.locale
布局:
res/layout/locale_order_list.xml
android:id="@+id/dragList"
src/com/android/settings/localepicker/LocaleListEditor.java
final RecyclerView list = (RecyclerView) view.findViewById(R.id.dragList);
语言切换事件:
packages\apps\Settings\src\com\android\settings\localepicker\LocaleRecyclerView.java
@Override public boolean onTouchEvent(MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_UP) { LocaleDragAndDropAdapter adapter = (LocaleDragAndDropAdapter) this.getAdapter(); if (adapter != null) { adapter.doTheUpdate(); } } return super.onTouchEvent(e); }
packages\apps\Settings\src\com\android\settings\localepicker\LocaleDragAndDropAdapter.java
public void doTheUpdate() { int count = mFeedItemList.size(); final Locale[] newList = new Locale[count]; for (int i = 0; i < count; i++) { final LocaleStore.LocaleInfo li = mFeedItemList.get(i); newList[i] = li.getLocale(); } final LocaleList ll = new LocaleList(newList); updateLocalesWhenAnimationStops(ll); }
public void updateLocalesWhenAnimationStops(final LocaleList localeList) { if (localeList.equals(mLocalesToSetNext)) { return; } // This will only update the Settings application to make things feel more responsive, // the system will be updated later, when animation stopped. LocaleList.setDefault(localeList); mLocalesToSetNext = localeList; final RecyclerView.ItemAnimator itemAnimator = mParentView.getItemAnimator(); itemAnimator.isRunning(new RecyclerView.ItemAnimator.ItemAnimatorFinishedListener() { @Override public void onAnimationsFinished() { if (mLocalesToSetNext == null || mLocalesToSetNext.equals(mLocalesSetLast)) { // All animations finished, but the locale list did not change return; } LocalePicker.updateLocales(mLocalesToSetNext); mLocalesSetLast = mLocalesToSetNext; mLocalesToSetNext = null; mNumberFormatter = NumberFormat.getNumberInstance(Locale.getDefault()); } }); }
frameworks\base\core\java\com\android\internal\app\LocalePicker.java
/** * 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 = ActivityManagerNative.getDefault(); 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 } }
frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
// 实现更新类似以下调用关系 ----begin
ActivityManagerNative.getDefault().updateConfiguration(mConfiguration);--》
updateConfigurationLocked(values, null, false);boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale) { // pass UserHandle.USER_NULL as userId because we don't persist configuration for any user return updateConfigurationLocked(values, starting, initLocale, false, UserHandle.USER_NULL); }
/ /实现更新类似以下调用关系 ----end
----- updatePersistentConfiguration begin
@Override public void updatePersistentConfiguration(Configuration values) { enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); enforceWriteSettingsPermission("updateConfiguration()"); if (values == null) { throw new NullPointerException("Configuration must not be null"); } int userId = UserHandle.getCallingUserId(); synchronized(this) { final long origId = Binder.clearCallingIdentity(); updateConfigurationLocked(values, null, false, true, userId); Binder.restoreCallingIdentity(origId); } }
/** * Do either or both things: (1) change the current configuration, and (2) * make sure the given activity is running with the (now) current * configuration. Returns true if the activity has been left running, or * false if <var>starting</var> is being destroyed to match the new * configuration. * * @param userId is only used when persistent parameter is set to true to persist configuration * for that particular user */ private boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean initLocale, boolean persistent, int userId) { int changes = 0;boolean localeChanged = false;// 判断local是否改变 if((mConfiguration != null) && (values != null)){ Locale beforeLocale = (mConfiguration.getLocales()== null) ? null:mConfiguration.getLocales().get(0); Locale newLocale = (values.getLocales()== null) ? null:values.getLocales().get(0);。。。
if (values != null) { Configuration newConfig = new Configuration(mConfiguration); changes = newConfig.updateFrom(values); ----获取配置更新项。。。会发送msg, 设置persist.sys.locale// 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(configCopy);---JUMP TO
frameworks\base\core\java\android\app\ActivityThread.javapublic final void applyConfigurationToResources(Configuration config) { synchronized (mResourcesManager) { mResourcesManager.applyConfigurationToResourcesLocked(config, null); } }/** * The global configuration upon which all Resources are based. Multi-window Resources * apply their overrides to this configuration. */ private final Configuration mResConfiguration = new Configuration();public final boolean applyConfigurationToResourcesLocked(@NonNull Configuration config, @Nullable CompatibilityInfo compat) {int changes = mResConfiguration.updateFrom(config);Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);frameworks\base\core\java\android\content\res\ResourcesImpl.java---JUMP TO
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()), -----调用native层更新配置资源 mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, keyboardHidden, mConfiguration.navigation, width, height, mConfiguration.smallestScreenWidthDp, mConfiguration.screenWidthDp, mConfiguration.screenHeightDp, mConfiguration.screenLayout, mConfiguration.uiMode, Build.VERSION.RESOURCES_SDK_INT);---JUMP BACK
for (int i = mResourceImpls.size() - 1; i >= 0; i--) { ----分别针对每一个配置更新调用 ResourcesKey key = mResourceImpls.keyAt(i); ResourcesImpl r = mResourceImpls.valueAt(i).get();r.updateConfiguration(tmpConfig, dm, compat); ----- 调用 mAssets.setConfiguration(}---JUMP BACK
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG); ---如果是永久性设置字体大小、语言,发送消息final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0; if (isDensityChange) { // Reset the unsupported display size dialog. mUiHandler.sendEmptyMessage(SHOW_UNSUPPORTED_DISPLAY_SIZE_DIALOG_MSG); ---屏膜密度改变 killAllBackgroundProcessesExcept(Build.VERSION_CODES.N, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE); }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 " + mConfiguration); app.thread.scheduleConfigurationChanged(configCopy); ----对最近运行列表中每个进程发送消息 } } catch (Exception e) { } }public void scheduleConfigurationChanged(Configuration config) { updatePendingConfiguration(config); sendMessage(H.CONFIGURATION_CHANGED, config); ----发送消息 }Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED); ----发送广播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) { -----语言改变发送广播 // Tell the shortcut manager that the system locale changed. It needs to know // it before any other apps receive ACTION_LOCALE_CHANGED, which is why // we "push" from here, rather than having the service listen to the broadcast. final ShortcutServiceInternal shortcutService = LocalServices.getService(ShortcutServiceInternal.class); if (shortcutService != null) { shortcutService.onSystemLocaleChangedNoLock(); } 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(); } if (starting != null) { kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false); -----更新activity 栈 // And we need to make sure at this point that all other activities // are made visible with the correct configuration. mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes, !PRESERVE_WINDOWS); } }}----- updatePersistentConfiguration end
frameworks\base\core\java\android\app\ResourcesManager.javaframeworks\base\core\java\android\content\res\Configuration.java---系统配置项,如字体、mcc、mnc、键盘、屏膜方向等
frameworks\base\core\java\android\content\pm\ActivityInfo.java
/** * Information you can retrieve about a particular application * activity or receiver. This corresponds to information collected * from the AndroidManifest.xml's <activity> and * <receiver> tags. */
/** * A style resource identifier (in the package's resources) of this * activity's theme. From the "theme" attribute or, if not set, 0. */ public int theme;
/** * M: Theme feature * Bit in {@link #configChanges} that indicates that the activity * can itself handle changes to the skin. Set from the * {@link android.R.attr#configChanges} attribute. * @hide */ public static final int CONFIG_SKIN = 0x80000000;
http://blog.csdn.net/pengyu1801/article/details/51176279