Android-語言設置流程分析

Android手機語言切換行為,是通過設置-語言和輸入法-語言來改變手機的語言,其實這個功能很少被用戶使用。

以Android5.1工程源碼為基礎,從設置app入手來分析和學習語言切換的過程:
一、語言設置界面:
首先在設置app中找到語言設置這個Preference,目前設置中界面大多都是Fragment,先找到語言和輸入法的PreferenceScreen,與其對應的Fragment是InputMethodAndLanguageSettings.java,在其onCreate()方法中,首先是增加語言設置的preference:? ??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. addPreferencesFromResource(R.xml.language_settings);??
找到language_settings.xml,可發現如下代碼:? ?
[html]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. <PreferenceScreen??
  2. ????????????android:key="phone_language"??
  3. ????????????android:title="@string/phone_language"??
  4. ????????????android:fragment="com.android.settings.LocalePicker"??
  5. ????????????/>??
于是斷定LocalePicker就是語言設置的Fragment,它是ListFragment的子類,繼承于framework中LocalePicker,并實現了父類的一個接口,其回調方法是onLocaleSelected(),Locale中文含義大致是語言環境,所以可推測這是設置語言后的一個回調方法,不確定的話,可打斷點測試一下。然而此類中并沒有關于語言設置界面數據適配的太多邏輯,只是通過父類的方法創建了一個view:? ?
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. @Override??
  2. ???public?View?onCreateView(??
  3. ???????????LayoutInflater?inflater,?ViewGroup?container,?Bundle?savedInstanceState)?{??
  4. ???????final?View?view?=?super.onCreateView(inflater,?container,?savedInstanceState);??
  5. ???????final?ListView?list?=?(ListView)?view.findViewById(android.R.id.list);??
  6. ???????Utils.forcePrepareCustomPreferencesList(container,?view,?list,?false);??
  7. ???????return?view;??
  8. ???}??
所以更多邏輯應該在framework中的LocalePicker.java中。既然是ListFragment,那就必須有Adapter,在此類中有構建了一個Adapter:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ????*?Constructs?an?Adapter?object?containing?Locale?information.?Content?is?sorted?by?
  3. ????*?{@link?LocaleInfo#label}.?
  4. ????*/??
  5. ???public?static?ArrayAdapter<LocaleInfo>?constructAdapter(Context?context)?{??
  6. ???????return?constructAdapter(context,?R.layout.locale_picker_item,?R.id.locale);??
  7. ???}??
  8. ???public?static?ArrayAdapter<LocaleInfo>?constructAdapter(Context?context,??
  9. ???????????final?int?layoutId,?final?int?fieldId)?{??
  10. ???????boolean?isInDeveloperMode?=?Settings.Global.getInt(context.getContentResolver(),??
  11. ???????????????Settings.Global.DEVELOPMENT_SETTINGS_ENABLED,?0)?!=?0;??
  12. ???????//獲取系統支持語言的信息??
  13. ???????final?List<LocaleInfo>?localeInfos?=?getAllAssetLocales(context,?isInDeveloperMode);??
  14. ???????final?LayoutInflater?inflater?=??
  15. ???????????????(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);??
  16. ???????return?new?ArrayAdapter<LocaleInfo>(context,?layoutId,?fieldId,?localeInfos)?{??
  17. ???????????@Override??
  18. ???????????public?View?getView(int?position,?View?convertView,?ViewGroup?parent)?{??
  19. ???????????????View?view;??
  20. ???????????????TextView?text;??
  21. ???????????????if?(convertView?==?null)?{??
  22. ???????????????????view?=?inflater.inflate(layoutId,?parent,?false);??
  23. ???????????????????text?=?(TextView)?view.findViewById(fieldId);??
  24. ???????????????????view.setTag(text);??
  25. ???????????????}?else?{??
  26. ???????????????????view?=?convertView;??
  27. ???????????????????text?=?(TextView)?view.getTag();??
  28. ???????????????}??
  29. ???????????????LocaleInfo?item?=?getItem(position);??
  30. ???????????????text.setText(item.toString());??
  31. ???????????????text.setTextLocale(item.getLocale());??
  32. ???????????????return?view;??
  33. ???????????}??
  34. ???????};??
  35. ???}??
而此方法通過getAllAssetLocales()方法獲取系統支持語言的信息:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. public?static?List<LocaleInfo>?getAllAssetLocales(Context?context,?boolean?isInDeveloperMode)?{??
  2. ????????final?Resources?resources?=?context.getResources();??
  3. ????????//獲取系統所支持的語言??
  4. ????????final?String[]?locales?=?Resources.getSystem().getAssets().getLocales();??
  5. ????????List<String>?localeList?=?new?ArrayList<String>(locales.length);??
  6. ????????Collections.addAll(localeList,?locales);??
  7. ??
  8. ????????//?Don't?show?the?pseudolocales?unless?we're?in?developer?mode.???
  9. ????????if?(!isInDeveloperMode)?{??
  10. ????????????localeList.remove("ar-XB");??
  11. ????????????localeList.remove("en-XA");??
  12. ????????}??
  13. ??
  14. ????????Collections.sort(localeList);??
  15. ????????final?String[]?specialLocaleCodes?=?resources.getStringArray(R.array.special_locale_codes);??
  16. ????????final?String[]?specialLocaleNames?=?resources.getStringArray(R.array.special_locale_names);??
  17. ??
  18. ????????final?ArrayList<LocaleInfo>?localeInfos?=?new?ArrayList<LocaleInfo>(localeList.size());??
  19. ????????for?(String?locale?:?localeList)?{??
  20. ????????????final?Locale?l?=?Locale.forLanguageTag(locale.replace('_',?'-'));??
  21. ????????????if?(l?==?null?||?"und".equals(l.getLanguage())??
  22. ????????????????????||?l.getLanguage().isEmpty()?||?l.getCountry().isEmpty())?{??
  23. ????????????????continue;??
  24. ????????????}??
  25. ??
  26. ????????????if?(localeInfos.isEmpty())?{??
  27. ????????????????if?(DEBUG)?{??
  28. ????????????????????Log.v(TAG,?"adding?initial?"+?toTitleCase(l.getDisplayLanguage(l)));??
  29. ????????????????}??
  30. ????????????????localeInfos.add(new?LocaleInfo(toTitleCase(l.getDisplayLanguage(l)),?l));??
  31. ????????????}?else?{??
  32. ????????????????//?check?previous?entry:??
  33. ????????????????//??same?lang?and?a?country?->?upgrade?to?full?name?and??
  34. ????????????????//????insert?ours?with?full?name??
  35. ????????????????//??diff?lang?->?insert?ours?with?lang-only?name??
  36. ????????????????final?LocaleInfo?previous?=?localeInfos.get(localeInfos.size()?-?1);??
  37. ????????????????if?(previous.locale.getLanguage().equals(l.getLanguage())?&&??
  38. ????????????????????????!previous.locale.getLanguage().equals("zz"))?{??
  39. ????????????????????if?(DEBUG)?{??
  40. ????????????????????????Log.v(TAG,?"backing?up?and?fixing?"?+?previous.label?+?"?to?"?+??
  41. ????????????????????????????????getDisplayName(previous.locale,?specialLocaleCodes,?specialLocaleNames));??
  42. ????????????????????}??
  43. ????????????????????previous.label?=?toTitleCase(getDisplayName(??
  44. ????????????????????????????previous.locale,?specialLocaleCodes,?specialLocaleNames));??
  45. ????????????????????if?(DEBUG)?{??
  46. ????????????????????????Log.v(TAG,?"??and?adding?"+?toTitleCase(??
  47. ????????????????????????????????getDisplayName(l,?specialLocaleCodes,?specialLocaleNames)));??
  48. ????????????????????}??
  49. ????????????????????localeInfos.add(new?LocaleInfo(toTitleCase(??
  50. ????????????????????????????getDisplayName(l,?specialLocaleCodes,?specialLocaleNames)),?l));??
  51. ????????????????}?else?{??
  52. ????????????????????String?displayName?=?toTitleCase(l.getDisplayLanguage(l));??
  53. ????????????????????if?(DEBUG)?{??
  54. ????????????????????????Log.v(TAG,?"adding?"+displayName);??
  55. ????????????????????}??
  56. ????????????????????localeInfos.add(new?LocaleInfo(displayName,?l));??
  57. ????????????????}??
  58. ????????????}??
  59. ????????}??
  60. ??
  61. ????????Collections.sort(localeInfos);??
  62. ????????return?localeInfos;??
  63. ????}??
此方法中還會通過Resources.getSystem().getAssets().getLocales()去獲得系統支持的語言信息,然后添加LocaleInfo里邊,再通過Adapter適配到ListView中。getLocales()方法屬于類AssetManager.java:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ?????*?Get?the?locales?that?this?asset?manager?contains?data?for.?
  3. ?????*?
  4. ?????*?<p>On?SDK?21?(Android?5.0:?Lollipop)?and?above,?Locale?strings?are?valid?
  5. ?????*?<a?href="https://tools.ietf.org/html/bcp47">BCP-47</a>?language?tags?and?can?be?
  6. ?????*?parsed?using?{@link?java.util.Locale#forLanguageTag(String)}.?
  7. ?????*?
  8. ?????*?<p>On?SDK?20?(Android?4.4W:?Kitkat?for?watches)?and?below,?locale?strings?
  9. ?????*?are?of?the?form?{@code?ll_CC}?where?{@code?ll}?is?a?two?letter?language?code,?
  10. ?????*?and?{@code?CC}?is?a?two?letter?country?code.?
  11. ?????*/??
  12. ????public?native?final?String[]?getLocales();??
乍一看,是個native方法,那不就是跟JNI有關系了,所以只能到相應JNI目錄下去找了,路徑:android5.1\frameworks\base\core\jni,對應文件:android_util_AssetManager.cpp(瀏覽下這個文件,發現這個家伙有點不得了啊,什么resource,theme等都跟它有關系,看樣子還的加油學學JNI啊!),然后找到對應的native方法:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. static?jobjectArray?android_content_AssetManager_getLocales(JNIEnv*?env,?jobject?clazz)??
  2. {??
  3. ????Vector<String8>?locales;??
  4. ??
  5. ????AssetManager*?am?=?assetManagerForJavaObject(env,?clazz);??
  6. ????if?(am?==?NULL)?{??
  7. ????????return?NULL;??
  8. ????}??
  9. ??
  10. ????am->getLocales(&locales);??
  11. ??
  12. ????const?int?N?=?locales.size();??
  13. ??
  14. ????jobjectArray?result?=?env->NewObjectArray(N,?g_stringClass,?NULL);??
  15. ????if?(result?==?NULL)?{??
  16. ????????return?NULL;??
  17. ????}??
  18. ??
  19. ????for?(int?i=0;?i<N;?i++)?{??
  20. ????????jstring?str?=?env->NewStringUTF(locales[i].string());??
  21. ????????if?(str?==?NULL)?{??
  22. ????????????return?NULL;??
  23. ????????}??
  24. ????????env->SetObjectArrayElement(result,?i,?str);??
  25. ????????env->DeleteLocalRef(str);??
  26. ????}??
  27. ??
  28. ????return?result;??
  29. }??
通過上面初步的分析,語言的List界面就基本出來了,在getAllAssetLocales()方法中打了個斷點,查看了下locales被賦值以后的值:
二、語言設置功能實現過程:
上面提到了設置中的LocalePicker類實現了父類接口中的onLocaleSelected()方法:? ?
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. public?static?interface?LocaleSelectionListener?{??
  2. ???????//?You?can?add?any?argument?if?you?really?need?it...??
  3. ???????public?void?onLocaleSelected(Locale?locale);??
  4. ???}??????
  5. ?????
  6. ???@Override??
  7. ???public?void?onLocaleSelected(final?Locale?locale)?{??
  8. ???????if?(Utils.hasMultipleUsers(getActivity()))?{??
  9. ???????????mTargetLocale?=?locale;??
  10. ???????????showDialog(DLG_SHOW_GLOBAL_WARNING);??
  11. ???????}?else?{??
  12. ???????????getActivity().onBackPressed();??
  13. ???????????LocalePicker.updateLocale(locale);??
  14. ???????}??
  15. ???}??
此方法中最終調用了其父類的updateLocale()方法來更新系統的語言環境:??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ??*?Requests?the?system?to?update?the?system?locale.?Note?that?the?system?looks?halted?
  3. ??*?for?a?while?during?the?Locale?migration,?so?the?caller?need?to?take?care?of?it.?
  4. ??*/??
  5. ?public?static?void?updateLocale(Locale?locale)?{??
  6. ?????try?{??
  7. ?????????IActivityManager?am?=?ActivityManagerNative.getDefault();??
  8. ?????????Configuration?config?=?am.getConfiguration();??
  9. ?????????//?Will?set?userSetLocale?to?indicate?this?isn't?some?passing?default?-?the?user??
  10. ?????????//?wants?this?remembered??
  11. ?????????config.setLocale(locale);??
  12. ?????????am.updateConfiguration(config);??
  13. ?????????//?Trigger?the?dirty?bit?for?the?Settings?Provider.??
  14. ?????????BackupManager.dataChanged("com.android.providers.settings");??
  15. ?????}?catch?(RemoteException?e)?{??
  16. ?????????//?Intentionally?left?blank??
  17. ?????}??
  18. ?}??
又看到ActivityManagerNative.getDefault(),所以可以直接到ActivityManagerService.java中找對應的方法,此方法中先是把選擇的語言設置到Configuration中,記錄下來。設置了不代表系統就知道這檔子事,所以還需要am去更新一下,說的俗氣一點:am老大知道了這檔子事,然后大吼一聲,我這里有個東西改變了,小伙伴們刷新一下!在ActivityManagerService中找到updateConfiguration()方法:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. public?void?updateConfiguration(Configuration?values)?{??
  2. ????????enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION,??
  3. ????????????????"updateConfiguration()");??
  4. ????????synchronized(this)?{??
  5. ????????????if?(values?==?null?&&?mWindowManager?!=?null)?{??
  6. ????????????????//?sentinel:?fetch?the?current?configuration?from?the?window?manager??
  7. ????????????????values?=?mWindowManager.computeNewConfiguration();??
  8. ????????????}??
  9. ????????????if?(mWindowManager?!=?null)?{??
  10. ????????????????mProcessList.applyDisplaySize(mWindowManager);??
  11. ????????????}??
  12. ????????????final?long?origId?=?Binder.clearCallingIdentity();??
  13. ????????????if?(values?!=?null)?{??
  14. ????????????????Settings.System.clearConfiguration(values);??
  15. ????????????}??
  16. ????????????updateConfigurationLocked(values,?null,?false,?false);??
  17. ????????????Binder.restoreCallingIdentity(origId);??
  18. ????????}??
  19. ????}??
看到Settings.System.clearConfiguration(values)不要以為這里把values清除了額,其實這個方法只是把系統字體的特效清除了,比如字體的大小:? ?
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ?????????*?@hide?Erase?the?fields?in?the?Configuration?that?should?be?applied?
  3. ?????????*?by?the?settings.?
  4. ?????????*/??
  5. ????????public?static?void?clearConfiguration(Configuration?inoutConfig)?{??
  6. ????????????inoutConfig.fontScale?=?0;??
  7. ????????}??
然后調用updateConfigurationLocked()方法:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ?????*?Do?either?or?both?things:?(1)?change?the?current?configuration,?and?(2)?
  3. ?????*?make?sure?the?given?activity?is?running?with?the?(now)?current?
  4. ?????*?configuration.??Returns?true?if?the?activity?has?been?left?running,?or?
  5. ?????*?false?if?<var>starting</var>?is?being?destroyed?to?match?the?new?
  6. ?????*?configuration.?
  7. ?????*?@param?persistent?TODO?
  8. ?????*/??
  9. boolean?updateConfigurationLocked(Configuration?values,??
  10. ????????????ActivityRecord?starting,?boolean?persistent,?boolean?initLocale)?{??
  11. ????????int?changes?=?0;??
  12. ????????if?(values?!=?null)?{??
  13. ????????????Configuration?newConfig?=?new?Configuration(mConfiguration);??
  14. ????????????changes?=?newConfig.updateFrom(values);??
  15. ????????????if?(changes?!=?0)?{??
  16. ????????????????if?(DEBUG_SWITCH?||?DEBUG_CONFIGURATION)?{??
  17. ????????????????????Slog.i(TAG,?"Updating?configuration?to:?"?+?values);??
  18. ????????????????}??
  19. ?????????????????
  20. ????????????????EventLog.writeEvent(EventLogTags.CONFIGURATION_CHANGED,?changes);??
  21. ????????????????if?(values.locale?!=?null?&&?!initLocale)?{??
  22. ????????????????????saveLocaleLocked(values.locale,??
  23. ?????????????????????????????????????!values.locale.equals(mConfiguration.locale),??
  24. ?????????????????????????????????????values.userSetLocale);??
  25. ????????????????}??
  26. ????????????????mConfigurationSeq++;??
  27. ????????????????if?(mConfigurationSeq?<=?0)?{??
  28. ????????????????????mConfigurationSeq?=?1;??
  29. ????????????????}??
  30. ????????????????newConfig.seq?=?mConfigurationSeq;??
  31. ????????????????mConfiguration?=?newConfig;??
  32. ????????????????Slog.i(TAG,?"Config?changes="?+?Integer.toHexString(changes)?+?"?"?+?newConfig);??
  33. ????????????????mUsageStatsService.reportConfigurationChange(newConfig,?mCurrentUserId);??
  34. ????????????????//mUsageStatsService.noteStartConfig(newConfig);??
  35. ????????????????final?Configuration?configCopy?=?new?Configuration(mConfiguration);??
  36. ?????????????????
  37. ????????????????//?TODO:?If?our?config?changes,?should?we?auto?dismiss?any?currently??
  38. ????????????????//?showing?dialogs???
  39. ????????????????mShowDialogs?=?shouldShowDialogs(newConfig);??
  40. ????????????????AttributeCache?ac?=?AttributeCache.instance();??
  41. ????????????????if?(ac?!=?null)?{??
  42. ????????????????????ac.updateConfiguration(configCopy);??
  43. ????????????????}??
  44. ????????????????//?Make?sure?all?resources?in?our?process?are?updated??
  45. ????????????????//?right?now,?so?that?anyone?who?is?going?to?retrieve??
  46. ????????????????//?resource?values?after?we?return?will?be?sure?to?get??
  47. ????????????????//?the?new?ones.??This?is?especially?important?during??
  48. ????????????????//?boot,?where?the?first?config?change?needs?to?guarantee??
  49. ????????????????//?all?resources?have?that?config?before?following?boot??
  50. ????????????????//?code?is?executed.??
  51. ????????????????mSystemThread.applyConfigurationToResources(configCopy);??
  52. ????????????????if?(persistent?&&?Settings.System.hasInterestingConfigurationChanges(changes))?{??
  53. ????????????????????Message?msg?=?mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);??
  54. ????????????????????msg.obj?=?new?Configuration(configCopy);??
  55. ????????????????????mHandler.sendMessage(msg);??
  56. ????????????????}??
  57. ????????????????for?(int?i=mLruProcesses.size()-1;?i>=0;?i--)?{??
  58. ????????????????????ProcessRecord?app?=?mLruProcesses.get(i);??
  59. ????????????????????try?{??
  60. ????????????????????????if?(app.thread?!=?null)?{??
  61. ????????????????????????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Sending?to?proc?"??
  62. ????????????????????????????????????+?app.processName?+?"?new?config?"?+?mConfiguration);??
  63. ????????????????????????????app.thread.scheduleConfigurationChanged(configCopy);??
  64. ????????????????????????}??
  65. ????????????????????}?catch?(Exception?e)?{??
  66. ????????????????????}??
  67. ????????????????}??
  68. ????????????????Intent?intent?=?new?Intent(Intent.ACTION_CONFIGURATION_CHANGED);??
  69. ????????????????intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY??
  70. ????????????????????????|?Intent.FLAG_RECEIVER_REPLACE_PENDING??
  71. ????????????????????????|?Intent.FLAG_RECEIVER_FOREGROUND);??
  72. ????????????????broadcastIntentLocked(null,?null,?intent,?null,?null,?0,?null,?null,??
  73. ????????????????????????null,?AppOpsManager.OP_NONE,?false,?false,?MY_PID,??
  74. ????????????????????????Process.SYSTEM_UID,?UserHandle.USER_ALL);??
  75. ????????????????if?((changes&ActivityInfo.CONFIG_LOCALE)?!=?0)?{??
  76. ????????????????????intent?=?new?Intent(Intent.ACTION_LOCALE_CHANGED);??
  77. ????????????????????intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);??
  78. ????????????????????broadcastIntentLocked(null,?null,?intent,??
  79. ????????????????????????????null,?null,?0,?null,?null,?null,?AppOpsManager.OP_NONE,??
  80. ????????????????????????????false,?false,?MY_PID,?Process.SYSTEM_UID,?UserHandle.USER_ALL);??
  81. ????????????????}??
  82. ????????????}??
  83. ????????}??
  84. ????????boolean?kept?=?true;??
  85. ????????final?ActivityStack?mainStack?=?mStackSupervisor.getFocusedStack();??
  86. ????????//?mainStack?is?null?during?startup.??
  87. ????????if?(mainStack?!=?null)?{??
  88. ????????????if?(changes?!=?0?&&?starting?==?null)?{??
  89. ????????????????//?If?the?configuration?changed,?and?the?caller?is?not?already??
  90. ????????????????//?in?the?process?of?starting?an?activity,?then?find?the?top??
  91. ????????????????//?activity?to?check?if?its?configuration?needs?to?change.??
  92. ????????????????starting?=?mainStack.topRunningActivityLocked(null);??
  93. ????????????}??
  94. ????????????if?(starting?!=?null)?{??
  95. ????????????????kept?=?mainStack.ensureActivityConfigurationLocked(starting,?changes);??
  96. ????????????????//?And?we?need?to?make?sure?at?this?point?that?all?other?activities??
  97. ????????????????//?are?made?visible?with?the?correct?configuration.??
  98. ????????????????mStackSupervisor.ensureActivitiesVisibleLocked(starting,?changes);??
  99. ????????????}??
  100. ????????}??
  101. ????????if?(values?!=?null?&&?mWindowManager?!=?null)?{??
  102. ????????????mWindowManager.setNewConfiguration(mConfiguration);??
  103. ????????}??
  104. ????????return?kept;??
  105. ????}??
此方法主要做兩件事:第一,改變當前的configuration,將新的數據放進去。第二,保證正在運行的應用程序界面更新最新的configuration。先調用updateFrom()方法,遍歷configuration包含的屬性是否改變,如果有改變就返回一個對應的整數,如果沒有改變就返回0。就語言改變而言,根據上面的分析,configuration至少有3個屬性發生了改變:fontscale(之前沒有設置字體的效果就不會改變)、locale和布局的direction。
有了新的數據就要保存,保存在configuration中不是個事。對于Android系統而言,改變語言,有兩個地方的數據需要更新,一個是SystemProperties,另一個是數據庫。前者以鍵值對的形式存放數據,多用于System,后者保存于DataBase中,多用于應用程序獲取,算是對外開放的數據。上面方法中對這兩個地方都進行了數據保存操作:
1)SystemProperties:調用saveLocaleLocked()方法:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. /**?
  2. ?????*?Save?the?locale.??You?must?be?inside?a?synchronized?(this)?block.?
  3. ?????*/??
  4. ????private?void?saveLocaleLocked(Locale?l,?boolean?isDiff,?boolean?isPersist)?{??
  5. ????????if(isDiff)?{??
  6. ????????????SystemProperties.set("user.language",?l.getLanguage());??
  7. ????????????SystemProperties.set("user.region",?l.getCountry());??
  8. ????????}??
  9. ????????if(isPersist)?{??
  10. ????????????SystemProperties.set("persist.sys.language",?l.getLanguage());??
  11. ????????????SystemProperties.set("persist.sys.country",?l.getCountry());??
  12. ????????????SystemProperties.set("persist.sys.localevar",?l.getVariant());??
  13. ????????????mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG,?l));??
  14. ????????}??
  15. ????}??
2)database:調用Settings.System.putConfiguration()方法:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. if?(persistent?&&?Settings.System.hasInterestingConfigurationChanges(changes))?{??
  2. ???????????????????Message?msg?=?mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);??
  3. ???????????????????msg.obj?=?new?Configuration(configCopy);??
  4. ???????????????????mHandler.sendMessage(msg);??
  5. ???}??
  6. ???...??
  7. ???case?UPDATE_CONFIGURATION_MSG:?{??
  8. ???????????????final?ContentResolver?resolver?=?mContext.getContentResolver();??
  9. ???????????????Settings.System.putConfiguration(resolver,?(Configuration)msg.obj);??
  10. ???????????}?break;??
該保存的數據保存了,但是Resource還不知道這檔子事,因為Android代碼和資源是分開的,Resource不知道Configuration發生了變化,Resource就不會去加載正確的資源。所以接下來此方法調用了mSystemThread.applyConfigurationToResources(configCopy)來完成這件事,mSystemThread是一個ActivityThread對象,其初始化在ActivityManagerService的構造函數中完成:? ?
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. mSystemThread?=?ActivityThread.currentActivityThread();??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. //此方法屬于ActivityThread??
  2. ????public?final?void?applyConfigurationToResources(Configuration?config)?{??
  3. ????????synchronized?(mResourcesManager)?{??
  4. ????????????mResourcesManager.applyConfigurationToResourcesLocked(config,?null);??
  5. ????????}??
  6. ????}??
  7. ????//此方法屬于ResourcesManage??
  8. ????public?final?boolean?applyConfigurationToResourcesLocked(Configuration?config,??
  9. ????????????CompatibilityInfo?compat)?{??
  10. ????????if?(mResConfiguration?==?null)?{??
  11. ????????????mResConfiguration?=?new?Configuration();??
  12. ????????}??
  13. ????????if?(!mResConfiguration.isOtherSeqNewer(config)?&&?compat?==?null)?{??
  14. ????????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Skipping?new?config:?curSeq="??
  15. ????????????????????+?mResConfiguration.seq?+?",?newSeq="?+?config.seq);??
  16. ????????????return?false;??
  17. ????????}??
  18. ????????int?changes?=?mResConfiguration.updateFrom(config);??
  19. ????????flushDisplayMetricsLocked();??
  20. ????????DisplayMetrics?defaultDisplayMetrics?=?getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);??
  21. ????????if?(compat?!=?null?&&?(mResCompatibilityInfo?==?null?||??
  22. ????????????????!mResCompatibilityInfo.equals(compat)))?{??
  23. ????????????mResCompatibilityInfo?=?compat;??
  24. ????????????changes?|=?ActivityInfo.CONFIG_SCREEN_LAYOUT??
  25. ????????????????????|?ActivityInfo.CONFIG_SCREEN_SIZE??
  26. ????????????????????|?ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;??
  27. ????????}??
  28. ????????//?set?it?for?java,?this?also?affects?newly?created?Resources??
  29. ????????if?(config.locale?!=?null)?{??
  30. ????????????Locale.setDefault(config.locale);??
  31. ????????}??
  32. ????????Resources.updateSystemConfiguration(config,?defaultDisplayMetrics,?compat);??
  33. ????????ApplicationPackageManager.configurationChanged();??
  34. ????????//Slog.i(TAG,?"Configuration?changed?in?"?+?currentPackageName());??
  35. ????????Configuration?tmpConfig?=?null;??
  36. ????????for?(int?i=mActiveResources.size()-1;?i>=0;?i--)?{??
  37. ????????????ResourcesKey?key?=?mActiveResources.keyAt(i);??
  38. ????????????Resources?r?=?mActiveResources.valueAt(i).get();??
  39. ????????????if?(r?!=?null)?{??
  40. ????????????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Changing?resources?"??
  41. ????????????????????????+?r?+?"?config?to:?"?+?config);??
  42. ????????????????int?displayId?=?key.mDisplayId;??
  43. ????????????????boolean?isDefaultDisplay?=?(displayId?==?Display.DEFAULT_DISPLAY);??
  44. ????????????????DisplayMetrics?dm?=?defaultDisplayMetrics;??
  45. ????????????????final?boolean?hasOverrideConfiguration?=?key.hasOverrideConfiguration();??
  46. ????????????????if?(!isDefaultDisplay?||?hasOverrideConfiguration)?{??
  47. ????????????????????if?(tmpConfig?==?null)?{??
  48. ????????????????????????tmpConfig?=?new?Configuration();??
  49. ????????????????????}??
  50. ????????????????????tmpConfig.setTo(config);??
  51. ????????????????????if?(!isDefaultDisplay)?{??
  52. ????????????????????????dm?=?getDisplayMetricsLocked(displayId);??
  53. ????????????????????????applyNonDefaultDisplayMetricsToConfigurationLocked(dm,?tmpConfig);??
  54. ????????????????????}??
  55. ????????????????????if?(hasOverrideConfiguration)?{??
  56. ????????????????????????tmpConfig.updateFrom(key.mOverrideConfiguration);??
  57. ????????????????????}??
  58. ????????????????????r.updateConfiguration(tmpConfig,?dm,?compat);??
  59. ????????????????}?else?{??
  60. ????????????????????r.updateConfiguration(config,?dm,?compat);??
  61. ????????????????}??
  62. ????????????????//Slog.i(TAG,?"Updated?app?resources?"?+?v.getKey()??
  63. ????????????????//????????+?"?"?+?r?+?":?"?+?r.getConfiguration());??
  64. ????????????}?else?{??
  65. ????????????????//Slog.i(TAG,?"Removing?old?resources?"?+?v.getKey());??
  66. ????????????????mActiveResources.removeAt(i);??
  67. ????????????}??
  68. ????????}??
  69. ????????return?changes?!=?0;??
  70. ????}??
此方法中Resource和ApplicationPackageManager都會去更新configuration,configuration所包含的屬性都會遍歷到,該更新的數據更新,該清除的緩存清除。
到這里,第一件事算是做完了,就要做第二件事,讓新的configuration更新到所有界面,updateConfigurationLocked()方法通過遍歷保存在ProcessRecord中的進程,然后通過scheduleConfigurationChanged()方法更新它們的configuration:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. for?(int?i=mLruProcesses.size()-1;?i>=0;?i--)?{??
  2. ?????ProcessRecord?app?=?mLruProcesses.get(i);??
  3. ?????try?{??
  4. ??????????if?(app.thread?!=?null)?{??
  5. ??????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Sending?to?proc?"??
  6. ????????????????????????????????????+?app.processName?+?"?new?config?"?+?mConfiguration);??
  7. ???????????app.thread.scheduleConfigurationChanged(configCopy);??
  8. ?????????}??
  9. ?????}?catch?(Exception?e)?{??
  10. ?????}??
  11. ?}??
此處通過Binder機制調用ApplicationThreadNative.java中的scheduleConfigurationChanged()方法,最后調用到ActivityThread中的內部類ApplicationThread的scheduleConfigurationChanged()方法,函數調用堆棧如圖:
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. public?void?scheduleConfigurationChanged(Configuration?config)?{??
  2. ????????????updatePendingConfiguration(config);??
  3. ????????????sendMessage(H.CONFIGURATION_CHANGED,?config);??
  4. ????}??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. case?CONFIGURATION_CHANGED:??
  2. ????????????????????Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,?"configChanged");??
  3. ????????????????????mCurDefaultDisplayDpi?=?((Configuration)msg.obj).densityDpi;??
  4. ????????????????????handleConfigurationChanged((Configuration)msg.obj,?null);??
  5. ????????????????????Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);??
  6. ????????????????????break;??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. final?void?handleConfigurationChanged(Configuration?config,?CompatibilityInfo?compat)?{??
  2. ????????int?configDiff?=?0;??
  3. ????????synchronized?(mResourcesManager)?{??
  4. ????????????if?(mPendingConfiguration?!=?null)?{??
  5. ????????????????if?(!mPendingConfiguration.isOtherSeqNewer(config))?{??
  6. ????????????????????config?=?mPendingConfiguration;??
  7. ????????????????????mCurDefaultDisplayDpi?=?config.densityDpi;??
  8. ????????????????????updateDefaultDensity();??
  9. ????????????????}??
  10. ????????????????mPendingConfiguration?=?null;??
  11. ????????????}??
  12. ????????????if?(config?==?null)?{??
  13. ????????????????return;??
  14. ????????????}??
  15. ?????????????
  16. ????????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Handle?configuration?changed:?"??
  17. ????????????????????+?config);??
  18. ????????????mResourcesManager.applyConfigurationToResourcesLocked(config,?compat);??
  19. ????????????if?(mConfiguration?==?null)?{??
  20. ????????????????mConfiguration?=?new?Configuration();??
  21. ????????????}??
  22. ????????????if?(!mConfiguration.isOtherSeqNewer(config)?&&?compat?==?null)?{??
  23. ????????????????return;??
  24. ????????????}??
  25. ????????????configDiff?=?mConfiguration.diff(config);??
  26. ????????????mConfiguration.updateFrom(config);??
  27. ????????????config?=?applyCompatConfiguration(mCurDefaultDisplayDpi);??
  28. ????????}??
  29. ????????ArrayList<ComponentCallbacks2>?callbacks?=?collectComponentCallbacks(false,?config);??
  30. ????????freeTextLayoutCachesIfNeeded(configDiff);??
  31. ????????if?(callbacks?!=?null)?{??
  32. ????????????final?int?N?=?callbacks.size();??
  33. ????????????for?(int?i=0;?i<N;?i++)?{??
  34. ????????????????performConfigurationChanged(callbacks.get(i),?config);??
  35. ????????????}??
  36. ????????}??
  37. ????}??
到這里設置語言以后,代碼跑的流程就基本結束了,需要一提的是performConfigurationChanged()方法。為什么要提它呢?因為有時候寫應用的時候activity需要關注一些configChanged,如:android:configChanges="orientation|keyboardHidden|screenSize",然后重寫onConfigurationChanged()方法。然而觸發這個方法回調的觸發點在哪里呢?這里就以設置語言為例,設置語言觸發了configuration的改變。先來看下performConfigurationChanged()方法:? ??
[java]?view plaincopy
在CODE上查看代碼片派生到我的代碼片
  1. private?static?void?performConfigurationChanged(ComponentCallbacks2?cb,?Configuration?config)?{??
  2. ????????//?Only?for?Activity?objects,?check?that?they?actually?call?up?to?their??
  3. ????????//?superclass?implementation.??ComponentCallbacks2?is?an?interface,?so??
  4. ????????//?we?check?the?runtime?type?and?act?accordingly.??
  5. ????????Activity?activity?=?(cb?instanceof?Activity)???(Activity)?cb?:?null;??
  6. ????????if?(activity?!=?null)?{??
  7. ????????????activity.mCalled?=?false;??
  8. ????????}??
  9. ????????boolean?shouldChangeConfig?=?false;??
  10. ????????if?((activity?==?null)?||?(activity.mCurrentConfig?==?null))?{??
  11. ????????????shouldChangeConfig?=?true;??
  12. ????????}?else?{??
  13. ????????????//?If?the?new?config?is?the?same?as?the?config?this?Activity??
  14. ????????????//?is?already?running?with?then?don't?bother?calling??
  15. ????????????//?onConfigurationChanged??
  16. ????????????int?diff?=?activity.mCurrentConfig.diff(config);??
  17. ????????????if?(diff?!=?0)?{??
  18. ????????????????//?If?this?activity?doesn't?handle?any?of?the?config?changes??
  19. ????????????????//?then?don't?bother?calling?onConfigurationChanged?as?we're??
  20. ????????????????//?going?to?destroy?it.??
  21. ????????????????if?((~activity.mActivityInfo.getRealConfigChanged()?&?diff)?==?0)?{??
  22. ????????????????????shouldChangeConfig?=?true;??
  23. ????????????????}??
  24. ????????????}??
  25. ????????}??
  26. ????????if?(DEBUG_CONFIGURATION)?Slog.v(TAG,?"Config?callback?"?+?cb??
  27. ????????????????+?":?shouldChangeConfig="?+?shouldChangeConfig);??
  28. ????????if?(shouldChangeConfig)?{??
  29. ????????????cb.onConfigurationChanged(config);??
  30. ????????????if?(activity?!=?null)?{??
  31. ????????????????if?(!activity.mCalled)?{??
  32. ????????????????????throw?new?SuperNotCalledException(??
  33. ????????????????????????????"Activity?"?+?activity.getLocalClassName()?+??
  34. ????????????????????????"?did?not?call?through?to?super.onConfigurationChanged()");??
  35. ????????????????}??
  36. ????????????????activity.mConfigChangeFlags?=?0;??
  37. ????????????????activity.mCurrentConfig?=?new?Configuration(config);??
  38. ????????????}??
  39. ????????}??
  40. ????}??
如果configuration確實改變了,那么此方法中就會調用cb.onConfigurationChanged(config)。cb代表ComponentCallbacks2,而ComponentCallbacks2 又繼承于ComponentCallbacks,所以onConfigurationChanged()方法屬于ComponentCallbacks,同樣Activity類也實現了ComponentCallbacks2這個接口,如此一來這個回調的過程就連接上了。也充分說明了為什么在configuration改變以后,activity關注的config會回調其父類的onConfigurationChanged()方法。
最后就是廣播configuration改變了,updateConfigurationLocked()廣播了ACTION_CONFIGURATION_CHANGED和ACTION_LOCALE_CHANGED,使用的方法是broadcastIntentLocked(),此方法廣播成功返回BROADCAST_SUCCESS。具體就不多說了。

轉載于:https://www.cnblogs.com/Free-Thinker/p/5461523.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/457978.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/457978.shtml
英文地址,請注明出處:http://en.pswp.cn/news/457978.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

charles 安裝 ssl_最全面的解決Charles手機抓包的證書問題(步驟非常詳細)

源自公眾號文章: 徹底解決Charles手機抓包的證書問題簡介: Charles 抓包是日常開發當中經常會用到的技術, 在 Android 6 之前, 手機系統既信任系統內置的證書, 也信任用戶自己安裝的證書, 但是在 Android 7 之后, 卻發生了變化, 手機系統只信任系統內置的根證書. 當然了, 這是為…

oracle報錯:ORA-00054: 資源正忙,要求指定 NOWAIT

ORA-00054: 資源正忙, 但指定以 NOWAIT 方式獲取資源&#xff1a; --首先得到被鎖對象的session_idselect session_id from v$locked_object; --通過上面得到的session_id去取得v$session的sid和serial#&#xff0c;然后對該進程進行終止。--SELECT sid, serial#, username, o…

ARM中ROM,RAM,FLASH區別

RAM&#xff08;Random Access Memory&#xff09;的全名為隨機存取記憶體&#xff0c;它相當于PC機上的移動存儲&#xff0c;用來存儲和保存數據的。它在任何時候都可以讀寫&#xff0c;RAM通常是作為操作系統或其他正在運行程序的臨時存儲介質&#xff08;可稱作系統內存&…

excel 2007 vba與宏完全剖析_Excel宏VBA小技巧系列 | 分段加合

寫在前面的話 知識產權算是一個盛產數據的行業。專利啊商標啊著作啊&#xff0c;都有著錄項目。我們常說的專利分析、產業導航、企業導航、產業預警、競爭情報、技術綜述、知識產權評議等等&#xff0c;常規操作之一就要先處理著錄項目數據&#xff0c;然后再進行不同角度的分…

SecureCRT:保存輸出日志的方法

處理地址&#xff1a; http://blog.sina.com.cn/s/blog_64c1dd210101gzgz.html 或者&#xff1a; http://renchen.blog.51cto.com/4531967/1195862 重點在與設定文檔的文件名稱&#xff0c;里面有說明。 http://renchen.blog.51cto.com/4531967/1195862轉載于:https://www.cnbl…

redhat虛擬機安裝

做過好多使用VMware workstation虛擬機搭建的系統&#xff0c;這是我第一次使用Virtual Box&#xff0c;感覺跟Vmware差不多&#xff0c;我的本子的系統是win7 64位。 下面演示安裝的是在VirtualBox里安裝rhel 6.4 linux 64位系統。 一、VirtualBOX 版本。 二、虛擬機的配置。…

mysql 查看表v空間自增漲_MySQL InnoDB表空間加密

從 MySQL5.7.11開始&#xff0c;MySQL對InnoDB支持存儲在單獨表空間中的表的數據加密 。此功能為物理表空間數據文件提供靜態加密。該加密是在引擎內部數據頁級別的加密手段&#xff0c;在數據頁寫入文件系統時加密&#xff0c;加密用的是AES算法&#xff0c;而其解密是在從文件…

arm之mmu原理

實驗目的&#xff1a;啟用MMU&#xff0c;映射SDRAM的地址空間&#xff0c;操作虛擬地址實現“點燈大法”&#xff0c;借此掌握MMU的使用。實驗環境及說明&#xff1a;恒頤S3C2410開發板H2410。H2410核心板擴展有64MB的K4S561632 SDRAM(4M*16bit*4BANK)&#xff0c;地址范圍是0…

osal_start_timerEx(Lock_TaskID,SBP_START_DEVICE_EVT,SBP_PERIODIC_EVT_PERIOD)的理解

osal_start_timerEx(Lock_TaskID,SBP_START_DEVICE_EVT,SBP_PERIODIC_EVT_PERIOD)與osal_set_event(Music_TaskID,event)的區別是osal_start_timerEx周期性的一直推送事件周期時間由SBP_PERIODIC_EVT_PERIOD決定&#xff0c;而osal_set_event是只推送一次。osal_start_timerEx開…

ideaspringboot項目上傳服務器_PHP中使用 TUS 協議來實現可恢復文件上傳

曾經嘗試過用PHP上傳大文件嗎&#xff1f;想知道您是否可以從上次中斷的地方繼續上傳&#xff0c;而不會在遇到任何中斷的情況下再次重新上傳整個數據&#xff1f;如果您覺得這個場景很熟悉&#xff0c;請接著往下閱讀。文件上傳是我們幾乎所有現代Web項目中的一項很常見的任務…

無密碼登陸

server A /B以root賬戶在A上無密碼ssh到B方式一ON A:ssh-keygen -t dsa -P -f ~/.ssh/id_dsa cat ~/.ssh/id_dsa.pub >> ~/.ssh/authorized_keys chmod 644 ~/.ssh/authorized_keys service sshd restart ON B:ssh-keygen -t dsa -P -f ~/.ssh/id_dsa cat ~/.ssh/id_d…

嵌入式常見筆試題總結

預處理器&#xff08;Preprocessor&#xff09;1. 用預處理指令#define 聲明一個常數&#xff0c;用以表明1年中有多少秒&#xff08;忽略閏年問題&#xff09; #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL 我在這想看到幾件事情&#xff1a; 1). #define 語法的基本知識…

【轉】php利用mkdir創建多級目錄

先介紹一下 mkdir() 這個函數&#xff1a; mkdir($path,0777,true); 第一個參數&#xff1a;必須&#xff0c;代表要創建的多級目錄的路徑&#xff1b; 第二個參數&#xff1a;設定目錄的權限&#xff0c;默認是 0777&#xff0c;意味著最大可能的訪問權&#xff1b; 第三個參數…

java使用xml存儲數據_用存儲過程和 JAVA 寫報表數據源有什么弊端?

用存儲過程和 JAVA 寫報表數據源有什么弊端&#xff1f;跟著小編一起來一看一下吧&#xff01;我們在報表開發中經常會使用存儲過程準備數據&#xff0c;存儲過程支持分步計算&#xff0c;可以實現非常復雜的計算邏輯&#xff0c;為報表開發帶來便利。所以&#xff0c;報表開發…

GIT文件的三種狀態

對于任何一個文件&#xff0c;在 Git 內都只有三種狀態&#xff1a;已提交&#xff08;committed&#xff09;&#xff0c;已修改&#xff08;modified&#xff09;和已暫存&#xff08;staged&#xff09;。已提交表示該文件已經被安全地保存在本地數據庫 中了&#xff1b;已修…

嵌入式常見筆試題總結(2)

預處理器&#xff08;Preprocessor&#xff09;   1. 用預處理指令#define 聲明一個常數&#xff0c;用以表明1年中有多少秒&#xff08;忽略閏年問題&#xff09;   #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL   我在這想看到幾件事情&#xff1a;   1). #…

MAC OS上JAVA1.6 升級1.7,以及?maven3.2.1配置

一、我的MAC系統 預裝的Jdk是1.6&#xff0c;由于需要使用eclipse MARs 2版本&#xff0c;故需要升級到1.7 二、下載JAVA jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 不知道為什么直接下非常慢&#xff0c;后來用的迅雷就超級快…

sql server 創建唯一性非聚集索引語句_數據庫專題—索引原理

深入淺出數據庫索引原理參見:https://www.cnblogs.com/aspwebchh/p/6652855.html1.為什么給表加上主鍵&#xff1f;1.平時創建表的時候&#xff0c;都會給表加上主鍵。如果沒有主鍵的表&#xff0c;數據會一行行的排列在磁盤上&#xff0c;查找一個數據需要一條條的進行對比。而…

String,StringBuffer,StringBuilder區別

String 字符串常量StringBuffer 字符串變量&#xff08;線程安全&#xff09;StringBuilder 字符串變量&#xff08;非線程安全&#xff09; 簡要的說&#xff0c; String 類型和 StringBuffer 類型的主要性能區別其實在于 String 是不可變的對象, 因此在每次對 String 類型進行…

oracle數據庫更新語句_20_手把手教你學Python之操作數據庫

數據庫是數據的倉庫&#xff0c;將大量數據按照一定的方式組織并存儲起來&#xff0c;方便進行管理和維護&#xff0c;例如快速檢索和統計等。數據庫的主要特點&#xff1a;以一定的方式組織、存儲數據&#xff1b;能為多個用戶共享&#xff1b;與程序彼此獨立。……數據庫管理…