ComponentCallbacks接口的定义:
public interface ComponentCallbacks {
/**
* Called by the system when the device configuration changes while your
* component is running. Note that, unlike activities, other components
* are never restarted when a configuration changes: they must always deal
* with the results of the change, such as by re-retrieving resources.
*
* <p>At the time that this function has been called, your Resources
* object will have been updated to return resource values matching the
* new configuration.
*
* <p>For more information, read <a href="{@docRoot}guide/topics/resources/runtime-changes.html"
* >Handling Runtime Changes</a>.
*
* @param newConfig The new device configuration.
*/
void onConfigurationChanged(Configuration newConfig);
/**
* This is called when the overall system is running low on memory, and
* actively running processes should trim their memory usage. While
* the exact point at which this will be called is not defined, generally
* it will happen when all background process have been killed.
* That is, before reaching the point of killing processes hosting
* service and foreground UI that we would like to avoid killing.
*
* <p>You should implement this method to release
* any caches or other unnecessary resources you may be holding on to.
* The system will perform a garbage collection for you after returning from this method.
* <p>Preferably, you should implement {@link ComponentCallbacks2#onTrimMemory} from
* {@link ComponentCallbacks2} to incrementally unload your resources based on various
* levels of memory demands. That API is available for API level 14 and higher, so you should
* only use this {@link #onLowMemory} method as a fallback for older versions, which can be
* treated the same as {@link ComponentCallbacks2#onTrimMemory} with the {@link
* ComponentCallbacks2#TRIM_MEMORY_COMPLETE} level.</p>
*/
void onLowMemory();
}
public interface ComponentCallbacks2 extendsComponentCallbacks {
/**
* Called when the operating system has determined that it is a good
* time for a process to trim unneeded memory from its process. This will
* happen for example when it goes in the background and there is not enough
* memory to keep as many background processes running as desired. You
* should never compare to exact values of the level, since new intermediate
* values may be added -- you will typically want to compare if the value
* is greater or equal to a level you are interested in.
*
* <p>To retrieve the processes current trim level at any point, you can
* use {@link android.app.ActivityManager#getMyMemoryState
* ActivityManager.getMyMemoryState(RunningAppProcessInfo)}.
*
* @param level The context of the trim, giving a hint of the amount of
* trimming the application may like to perform. May be
* {@link #TRIM_MEMORY_COMPLETE}, {@link #TRIM_MEMORY_MODERATE},
* {@link #TRIM_MEMORY_BACKGROUND}, {@link #TRIM_MEMORY_UI_HIDDEN},
* {@link #TRIM_MEMORY_RUNNING_CRITICAL}, {@link #TRIM_MEMORY_RUNNING_LOW},
* or {@link #TRIM_MEMORY_RUNNING_MODERATE}.
*/
void onTrimMemory(int level);
}
onConfigurationChanged回调的逻辑在 AMS.updateConfiguration中实现。代码如下:
public voidupdateConfiguration(Configuration values) {
final long origId = Binder.clearCallingIdentity();
if (values != null) {
Settings.System.clearConfiguration(values);
}
updateConfigurationLocked(values, null, false, false);
Binder.restoreCallingIdentity(origId);
}
}
boolean updateConfigurationLocked(Configurationvalues,
ActivityRecord starting, boolean persistent, boolean initLocale) {
int changes = 0;
if (values != null) {
Configuration newConfig = new Configuration(mConfiguration);
changes = newConfig.updateFrom(values);
if (changes != 0) {
final Configuration configCopy = new Configuration(mConfiguration);
// TODO: If our config changes, should we auto dismiss any currently
// showing dialogs?
mShowDialogs = shouldShowDialogs(newConfig);
// 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); // 更新system server的configuration
if (persistent && Settings.System.hasInterestingConfigurationChanges(changes)) {
Message msg = mHandler.obtainMessage(UPDATE_CONFIGURATION_MSG);// 更新settings system中的设置
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); // 向每一个进程发送configuration changed的通知,进程会调用当前进程中,实现了ComponentCallbacks接口的成员(一般指Activity, Service, ContentProvider)
}
} 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); // 向系统中发送 configuration changed 广播
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); // 向系统中发送 locale changed 广播
}
}
}
boolean kept = true;
final ActivityStack mainStack = mStackSupervisor.getFocusedStack(); // 获得当前的stack
// 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); // 获得正在running的activity
}
if (starting != null) {
kept = mainStack.ensureActivityConfigurationLocked(starting, changes); // 确保top activity获得了最新的configuration
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
}
}
return kept;
}
final boolean ensureActivityConfigurationLocked(ActivityRecord r,
int globalChanges) {
// Okay we now are going to make this activity have the new config.
// But then we need to figure out how it needs to deal with that.
Configuration oldConfig = r.configuration;
r.configuration = newConfig; 更新当前activity的配置
// Determine what has changed. May be nothing, if this is a config
// that has come back from the app after going idle. In that case
// we just want to leave the official config object now in the
// activity and do nothing else.
final int changes = oldConfig.diff(newConfig);
if (changes == 0 && !r.forceNewConfig) { // 如果配置没有变化,则直接返回
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Configuration no differences in " + r);
return true;
}
// If the activity isn't currently running, just leave the new
// configuration and it will pick that up next time it starts.
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Configuration doesn't matter not running " + r);
r.stopFreezingScreenLocked(false);
r.forceNewConfig = false;
return true;
}
// Figure out how to handle the changes between the configurations.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) { 调式打印
Slog.v(TAG, "Checking to restart " + r.info.name + ": changed=0x"
+ Integer.toHexString(changes) + ", handles=0x"
+ Integer.toHexString(r.info.getRealConfigChanged())
+ ", newConfig=" + newConfig);
}
if ((changes&(~r.info.getRealConfigChanged())) != 0 || r.forceNewConfig) { // 配置有变化,并且activity does not declare to handle all the changes (Which changes activity want to handle is defined in android:configChanges attribute, e.g. "android:configChanges="orientation|keyboardHidden|screenSize")
// Aha, the activity isn't handling the change, so DIE DIE DIE.
r.configChangeFlags |= changes;
r.startFreezingScreenLocked(r.app, globalChanges);
r.forceNewConfig = false;
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Config is destroying non-running " + r);
destroyActivityLocked(r, true, "config"); 如果进程还没有运行,则直接destroy
} else if (r.state == ActivityState.PAUSING) {
// A little annoying: we are waiting for this activity to
// finish pausing. Let's not do anything now, but just
// flag that it needs to be restarted when done pausing.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Config is skipping already pausing " + r);
r.configDestroy = true;
return true;
} else if (r.state == ActivityState.RESUMED) {
// Try to optimize this case: the configuration is changing
// and we need to restart the top, resumed activity.
// Instead of doing the normal handshaking, just say
// "restart!".
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Config is relaunching resumed " + r);
relaunchActivityLocked(r, r.configChangeFlags, true); 重启已经running的activity
r.configChangeFlags = 0;
} else {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Config is relaunching non-resumed " + r);
relaunchActivityLocked(r, r.configChangeFlags, false); 重启已经非running的activity
r.configChangeFlags = 0;
}
// All done... tell the caller we weren't able to keep this
// activity around.
return false;
}
// Default case: the activity can handle this new configuration, so
// hand it over. Note that we don't need to give it the new
// configuration, since we always send configuration changes to all
// process when they happen so it can just use whatever configuration
// it last got.
if (r.app != null && r.app.thread != null) {
try {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending new config to " + r);
r.app.thread.scheduleActivityConfigurationChanged(r.appToken); 向指定的activity发送configuration change通知,最后会回调activity中的onConfigurationChanged。之前的scheduleConfigurationChanged也会回调onConfigurationChanged,但是在activity中有记录,不会重复调用
} catch (RemoteException e) {
// If process died, whatever.
}
}
r.stopFreezingScreenLocked(false);
return true;
}
ActivityThread中调用onConfigurationChanged的逻辑
scheduleConfigurationChanged --> handleConfigurationChanged
final voidhandleConfigurationChanged(Configuration config, CompatibilityInfo compat) {
int configDiff = 0;
synchronized (mResourcesManager) {
mResourcesManager.applyConfigurationToResourcesLocked(config, compat);// 更新Resource,使用对应于configuration的资源
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);// 收集ComponentCallbacks的实现类
if (callbacks != null) {
final int N = callbacks.size();
for (int i=0; i<N; i++) {
performConfigurationChanged(callbacks.get(i), config); // 调用每个实现类的onConfigurationChanged,并更新它的configuration
}
}
}
scheduleActivityConfigurationChanged --> handleActivityConfigurationChanged --> performConfigurationChanged
private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {
boolean shouldChangeConfig = false;
if ((activity == null) || (activity.mCurrentConfig == null)) { 如果activity之前没有configuration,则调用onConfigurationChanged更新
shouldChangeConfig = true;
} else {
int diff = activity.mCurrentConfig.diff(config);
if (diff != 0) { 如果新的configuration和旧的不一样
if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) { 如果activity的配置中申请了对 diff 的处理,则不重启该activity。
shouldChangeConfig = true;
}
}
}
if (shouldChangeConfig) { 如果不重启activity
cb.onConfigurationChanged(config); 回调onConfigurationChanged
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); 更新activity的当前config为新的config,保证如果下次的config不变的情况下,不会重复调用onConfigurationChanged
}
}
}