说到Configuration,大家最熟悉的想必是android:configChanges=[“mcc”, “mnc”, “locale”,”touchscreen”, “keyboard”, “keyboardHidden”,”navigation”,”screenLayout”, “fontScale”, “uiMode”,”orientation”, “screenSize”, “smallestScreenSize”],而这个属性了解的最多的就是在横竖屏切换的”orientation”。
在Activity中定义android:configChanges=”orientation”,这样当横竖屏切换时,可以在onConfigurationChanged监听到改变,同时Activity不会被重启。不知道大家是否想过,为什么如果不定义orientation时,系统需要重启Activity呢?
这还要从资源说起,Android应用程序在资源的定义中,会为不同的屏幕尺寸,语言,横竖屏定义不同的资源文件,存放不同的资源。以保证应用程序可以适配不同的屏幕,语言,横竖屏等等。而Android系统如何从当前应用程序中获取最合适的资源来显示呢?
下面是官网给的Android系统从应用程序中获取资源的流程:官方链接
图中第二步:table中包含MCC, MNC, Language等18个资源获取的维度,table说的是资源配置表,有了这个资源配置表,Android系统就可以从aapt打包生成的resources.arsc中通过上面图标中的步骤,找到最合适的资源。而这个资源配置表,就是通过Configuration来设置的。
再回到系统为什么需要重启Activity的问题上,比如当Configuration中语言(local)发生变化时,也就是说当前获取最佳资源的维度发生了变化,也就是说在Configuration变化之前已经启动的Activity上显示的资源已经不是最合适当前配置的资源了,因此系统需要更新资源配置,清除之前配置缓存的资源,然后重启已经启动的Activity以便显示更新Configuration配置的资源。
接下来就以语言的变化流程,看系统是如何更新配置,清除缓存资源,然后重启已经启动的Activity。
1 Setting应用中更换语言时:调用LocalePicker.updateLocale
public static void updateLocale(Locale locale) {
IActivityManager am = ActivityManagerNative.getDefault();
Configuration config = am.getConfiguration();
config.setLocale(locale);
config.userSetLocale = true;
am.updateConfiguration(config);
}
这个函数主要获取AMS代理对象,然后从AMS中获取Configuration对象config,设置config中改变的对象locale也就是语言,并设置userSetLocale为true, 最有通过AMS的代理对象调用AMS的updateConfiguration函数。
2 AMS的updateConfiguration函数主要是调用updateConfigurationLocked()函数。
这个函数比较长,分三个部分来分析:
1 初始化和其他设置阶段:
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 (!initLocale && values.locale != null && values.userSetLocale) {
final String languageTag = values.locale.toLanguageTag();
SystemProperties.set(“persist.sys.locale”, languageTag);
mHandler.sendMessage(mHandler.obtainMessage(SEND_LOCALE_TO_MOUNT_DAEMON_MSG, values.locale));
}
mConfigurationSeq++;
if (mConfigurationSeq <= 0) {
mConfigurationSeq = 1;
}
newConfig.seq = mConfigurationSeq;
mConfiguration = newConfig;
mUsageStatsService.reportConfigurationChange(newConfig, mCurrentUserId);
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);
msg.obj = new Configuration(configCopy);
mHandler.sendMessage(msg);
A 创建newConfig = new Configuration(mConfiguration);
在Configuration构造函数中会调用setTo函数,把mConfiguration的各个成员变量赋值到newConfig中。
在Configuration的成员变量中,包含mcc(移动国家编码), mnc(移动网络编码), locale(语言),keyboardHidden(键盘显隐),等等,每一个都是一个资源获取的维度,每一个都影响当前应用中最合适资源的匹配。
同时在ActivityInfo中还定义了,CONFIG_MCC,CONFIG_MNC,CONFIG_LOCALE等静态整型变量,这些整型变量与Configuration的成员变量一一对应,并最终以位的形式标记,新旧Configuration哪些成员变量发生了变化。
B 调用Configuration. updateFrom通过改变的Configuration对象values更新newConfig,同时返回整数change,change就是以位的形式标记values和newConfig哪些成员变量发生了变化,在语言设置的流程中必然整数change的CONFIG_LOCALE位为”1”,标记当前语言发生了变化。
C 因为Configuration的local成员变量发生了变化,所以change不为0,接下来,
通过发送消息SEND_LOCALE_TO_MOUNT_DAEMON_MSG 设置local到MountService,
设置newConfig 的mConfigurationSeq
提交newConfig 到UsageStatsService(统计服务)
发送消息UPDATE_CONFIGURATION_MSG 把改变的Configuration 保存到设置中。
二部分:通知各个应用进程,Configuration改变:
mSystemThread.applyConfigurationToResources(configCopy);
for (int i=mLruPro