生活随笔
收集整理的這篇文章主要介紹了
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
???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;????????????????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();??????????????????final?String[]?locales?=?Resources.getSystem().getAssets().getLocales();??????????List<String>?localeList?=?new?ArrayList<String>(locales.length);??????????Collections.addAll(localeList,?locales);????????????????????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?{??????????????????????????????????????????????????????????????????????????????????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
????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?{????????????????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
?public?static?void?updateLocale(Locale?locale)?{???????try?{???????????IActivityManager?am?=?ActivityManagerNative.getDefault();???????????Configuration?config?=?am.getConfiguration();?????????????????????????????config.setLocale(locale);???????????am.updateConfiguration(config);????????????????????BackupManager.dataChanged("com.android.providers.settings");???????}?catch?(RemoteException?e)?{????????????????}???}?? 又看到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)?{??????????????????????????????????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
????????public?static?void?clearConfiguration(Configuration?inoutConfig)?{??????????????inoutConfig.fontScale?=?0;??????????}?? 然后調(diào)用updateConfigurationLocked()方法:
[java]?view plaincopy
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);??????????????????????????????????final?Configuration?configCopy?=?new?Configuration(mConfiguration);???????????????????????????????????????????????????????????????????mShowDialogs?=?shouldShowDialogs(newConfig);??????????????????AttributeCache?ac?=?AttributeCache.instance();??????????????????if?(ac?!=?null)?{??????????????????????ac.updateConfiguration(configCopy);??????????????????}??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????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();??????????????????if?(mainStack?!=?null)?{??????????????if?(changes?!=?0?&&?starting?==?null)?{??????????????????????????????????????????????????????????????????starting?=?mainStack.topRunningActivityLocked(null);??????????????}??????????????if?(starting?!=?null)?{??????????????????kept?=?mainStack.ensureActivityConfigurationLocked(starting,?changes);??????????????????????????????????????????????????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
????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
????public?final?void?applyConfigurationToResources(Configuration?config)?{??????????synchronized?(mResourcesManager)?{??????????????mResourcesManager.applyConfigurationToResourcesLocked(config,?null);??????????}??????}??????????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;??????????}??????????????????if?(config.locale?!=?null)?{??????????????Locale.setDefault(config.locale);??????????}??????????Resources.updateSystemConfiguration(config,?defaultDisplayMetrics,?compat);??????????ApplicationPackageManager.configurationChanged();??????????????????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);??????????????????}??????????????????????????????????????????????}?else?{??????????????????????????????????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)?{??????????????????????????????????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?{??????????????????????????????????????????????????int?diff?=?activity.mCurrentConfig.diff(config);??????????????if?(diff?!=?0)?{??????????????????????????????????????????????????????????????????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ò),歡迎將生活随笔推薦給好友。