| android語(yǔ)言切換是在packages/apps/Settings/com/android/settings/LocalePicker.java的updateLocale()函數(shù)中調(diào)用. 從注釋可以看出, 只要本地local改變就會(huì)調(diào)用該函數(shù). 查看ActivityManagerNative的getDefault()可以看到, 該函數(shù)返回的是遠(yuǎn)程服務(wù)對(duì)象ActivityManagerServices.java在本地的一個(gè)代理.  最終調(diào)用的是ActivityManagerService.java中的updateConfiguration()函數(shù).         public static void updateLocale(Locale locale) {          try {              IActivityManager am = ActivityManagerNative.getDefault();              Configuration config = am.getConfiguration();                config.locale = locale;                              config.userSetLocale = true;                am.updateConfiguration(config);                            BackupManager.dataChanged("com.android.providers.settings");          } catch (RemoteException e) {                        }      }  
該函數(shù), 首先進(jìn)行的是權(quán)限的校驗(yàn). 然后調(diào)用updateConfigurationLocked()函數(shù).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);          }      }  
               public boolean updateConfigurationLocked(Configuration values,              ActivityRecord starting, boolean persistent, boolean initLocale) {          int changes = 0;                    boolean kept = true;                    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, values.simSetLocale);                  }                                      mConfigurationSeq++;                  if (mConfigurationSeq <= 0) {                      mConfigurationSeq = 1;                  }                  newConfig.seq = mConfigurationSeq;                  mConfiguration = newConfig;                  Slog.i(TAG, "Config changed: " + newConfig);                    final Configuration configCopy = new Configuration(mConfiguration);                    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);                  broadcastIntentLocked(null, null, intent, null, null, 0, null, null,                          null, false, false, MY_PID, Process.SYSTEM_UID);                  if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {                      broadcastIntentLocked(null, null,                              new Intent(Intent.ACTION_LOCALE_CHANGED),                              null, null, 0, null, null,                              null, false, false, MY_PID, Process.SYSTEM_UID);                  }                                }          }                    if (changes != 0 && starting == null) {                                                        starting = mMainStack.topRunningActivityLocked(null);          }                    if (starting != null) {              kept = mMainStack.ensureActivityConfigurationLocked(starting, changes);                                          mMainStack.ensureActivitiesVisibleLocked(starting, changes);          }                    if (values != null && mWindowManager != null) {              mWindowManager.setNewConfiguration(mConfiguration);          }                    return kept;      }  
整個(gè)語(yǔ)言切換就在這個(gè)函數(shù)中完成. 咋一看似乎沒(méi)感覺(jué)到該函數(shù)做了哪些事情. 我們首先來(lái)看注釋: Do either or both things: (1) change the current configuration, and (2)
 make sure the given activity is running with the (now) current. configuration大概意思是: 這個(gè)函數(shù)做了兩件事情. (1). 改變當(dāng)前的configuration. 意思就是讓改變的configuration更新到當(dāng)前configuration. (2) 確保所有正在運(yùn)行的activity都能更新改變后的configuration.(這點(diǎn)是關(guān)鍵.) . 我們按照這個(gè)思路看看android是如何更新configuration. 查看代碼
 , 首先看到 這個(gè)函數(shù)首先判斷values是否為空, 這里values肯定不為空的, 然后changes = newConfig.updateFrom(values); 我們看看updateFrom做了什么操作.
 
              public int updateFrom(Configuration delta) {          int changed = 0;          ...          if (delta.locale != null                  && (locale == null || !locale.equals(delta.locale))) {              changed |= ActivityInfo.CONFIG_LOCALE;              locale = delta.locale != null                       (Locale) delta.locale.clone() : null;              textLayoutDirection = LocaleUtil.getLayoutDirectionFromLocale(locale);          }          if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))          {              userSetLocale = true;              changed |= ActivityInfo.CONFIG_LOCALE;          }          ...          return changed;      }  
因?yàn)檎Z(yǔ)言改變了, 那么 (!locale.equals(delta.locale)) 是true. changed 大于0, 然后return changed. 回到ActivityManagerService.java的updateConfigurationLocked函數(shù), 因?yàn)閏hanged不為0 , 所以走if這個(gè)流程.  繼續(xù)看代碼 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) {                      }                  }  
 首先看到的是mLurProcesses 是ArrayList<ProcessRecord>類型.  LRU : Least Recently Used保存所有運(yùn)行過(guò)的進(jìn)程.  ProcessRecord進(jìn)程類, 一個(gè)apk文件運(yùn)行時(shí)會(huì)對(duì)應(yīng)一個(gè)進(jìn)程. app.thread. 此處的thread代表的是ApplicationThreadNative.java類型.  然后調(diào)用其scheduleConfigurationChanged();  查看該函數(shù)
 public final void scheduleConfigurationChanged(Configuration config)              throws RemoteException {          Parcel data = Parcel.obtain();          data.writeInterfaceToken(IApplicationThread.descriptor);          config.writeToParcel(data, 0);          mRemote.transact(SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION, data, null,                  IBinder.FLAG_ONEWAY);          data.recycle();      }  
又是通過(guò)binder調(diào)用, 所以 , binder在android中是一個(gè)很重要的概念. 此處遠(yuǎn)程調(diào)用的是ActivityThread.java中的私有內(nèi)部?jī)?nèi)ApplicationThread
   private class ApplicationThread extends ApplicationThreadNative {          private static final String HEAP_COLUMN = "%13s %8s %8s %8s %8s %8s %8s";          private static final String ONE_COUNT_COLUMN = "%21s %8d";          private static final String TWO_COUNT_COLUMNS = "%21s %8d %21s %8d";          private static final String TWO_COUNT_COLUMNS_DB = "%21s %8d %21s %8d";          private static final String DB_INFO_FORMAT = "  %8s %8s %14s %14s  %s";              ...          public void scheduleConfigurationChanged(Configuration config) {              updatePendingConfiguration(config);              queueOrSendMessage(H.CONFIGURATION_CHANGED, config);          }          ...  }  
而ApplicationThread中的handler的CONFIGURATION_CHANGED是調(diào)用handleConfigurationChanged()
 final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {           ArrayList<ComponentCallbacks2> callbacks = null;        ...         ...         applyConfigurationToResourcesLocked(config, compat);                  ...                  callbacks = collectComponentCallbacksLocked(false, config);         ...                  if (callbacks != null) {             final int N = callbacks.size();             for (int i=0; i<N; i++) {                 performConfigurationChanged(callbacks.get(i), config);             }         }  
 這個(gè)函數(shù)首先是調(diào)用applyConfigurationToResourcesLocked(). 看函數(shù)名大概可以推測(cè): 將configuration應(yīng)用到resources.這里configuration改變的是local 本地語(yǔ)言. 那而resources資源包含不就包含了語(yǔ)言, 圖片這些資源嗎.  final boolean applyConfigurationToResourcesLocked(Configuration config,              CompatibilityInfo compat) {                    int changes = mResConfiguration.updateFrom(config);          DisplayMetrics dm = getDisplayMetricsLocked(null, true);              if (compat != null && (mResCompatibilityInfo == null ||                  !mResCompatibilityInfo.equals(compat))) {              mResCompatibilityInfo = compat;              changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT                      | ActivityInfo.CONFIG_SCREEN_SIZE                      | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;          }            ...            Resources.updateSystemConfiguration(config, dm, compat);            ...                    Iterator<WeakReference<Resources>> it =              mActiveResources.values().iterator();          while (it.hasNext()) {              WeakReference<Resources> v = it.next();              Resources r = v.get();              if (r != null) {                  if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "                          + r + " config to: " + config);                  r.updateConfiguration(config, dm, compat);                                                  } else {                                    it.remove();              }          }                    return changes != 0;      }  
Resources.updateSystemConfiguration()清除一部分系統(tǒng)資源, 并且將config更新到Resources, 而Resources包含了一個(gè)AssetManager對(duì)象, 該對(duì)象的核心實(shí)現(xiàn)是在AssetManager.cpp中完成的. 然后循環(huán)清空mActivityResources資源. 再回到handleConfigurationChanged()函數(shù), 執(zhí)行完updateSystemConfiguration后, 會(huì)循環(huán)該進(jìn)程的所有activity: if (callbacks != null) {             final int N = callbacks.size();for (int i=0; i<N; i++) {
 performConfigurationChanged(callbacks.get(i), config);
 }
 }
 
 再來(lái)看performConfigurationChanged的實(shí)現(xiàn): private final 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);              }          }      }  
該函數(shù)判斷configuration是否改變, 如果改變那么shouldChangeConfig為true. 然后調(diào)用activity的onConfigurationChange(config);
                     public void onConfigurationChanged(Configuration newConfig) {         mCalled = true;           mFragments.dispatchConfigurationChanged(newConfig);           if (mWindow != null) {                          mWindow.onConfigurationChanged(newConfig);         }           if (mActionBar != null) {                                       mActionBar.onConfigurationChanged(newConfig);         }     }  
 查看注釋, 大概意思是:  如果你的activity運(yùn)行 , 設(shè)備信息有改變(即configuration改變)時(shí)由系統(tǒng)調(diào)用. 如果你在manifest.xml中配置了configChnages屬性則表示有你自己來(lái)處理configuration change. 否則就重啟當(dāng)前這個(gè)activity.  而重啟之前, 舊的resources已經(jīng)被清空, 那么就會(huì)裝載新的資源, 整個(gè)過(guò)程就完成了語(yǔ)言切換后 , 能夠讓所有app使用新的語(yǔ)言. 語(yǔ)言切換流程大概分為三步: 第一步:  判斷configuration的local是否已經(jīng)改變, 如果改變則將local更新到當(dāng)前的configuration 第二步: 清空舊的資源.  第三步: 重啟所有所有進(jìn)程并加裝新資源. 由于個(gè)人知識(shí)水平有限, 有些地方不免有些紕漏, 希望大牛多多指點(diǎn).  也希望有共同興趣愛(ài)好的人進(jìn)行技術(shù)交流. |