日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android-语言设置流程分析

發(fā)布時(shí)間:2023/12/18 Android 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android-语言设置流程分析 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Android手機(jī)語(yǔ)言切換行為,是通過設(shè)置-語(yǔ)言和輸入法-語(yǔ)言來(lái)改變手機(jī)的語(yǔ)言,其實(shí)這個(gè)功能很少被用戶使用。

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

    轉(zhuǎn)載于:https://www.cnblogs.com/Free-Thinker/p/5461523.html

    總結(jié)

    以上是生活随笔為你收集整理的Android-语言设置流程分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。