内容简介:当设备配置发生变更时,系统会调用AMS的可以看到可以看到,决定返回值的是
当设备配置发生变更时,系统会调用AMS的 updateConfiguration()
方法,来通知AMS处理configuration changed事件, updateConfiguration()
源码如下:
public boolean updateConfiguration(Configuration values) {
// ... 省略一段代码
synchronized(this) {
// ... 省略一段代码
try {
if (values != null) {
Settings.System.clearConfiguration(values);
}
updateConfigurationLocked(values, null, false, false /* persistent */,
UserHandle.USER_NULL, false /* deferResume */,
mTmpUpdateConfigurationResult);
return mTmpUpdateConfigurationResult.changes != 0;
} finally {
Binder.restoreCallingIdentity(origId);
}
}
}
复制代码
可以看到 updateConfiguration()
内部调用了 private boolean updateConfigurationLocked()
,在其代码注释中我们可以看到,该方法一共做了两件事:
1.调用 updateGlobalConfigurationLocked()
更新当前配置信息
2.调用 ensureConfigAndVisibilityAfterUpdate()
确保给定的activity使用的是当前配置
如果返回true表示activity未被重启,否则让该activity destroyed以适配当前配置。 ensureConfigAndVisibilityAfterUpdate()
的源码如下:
private boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
boolean kept = true;
// 获取当前拥有焦点的activity
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();
}
if (starting != null) {
// 关键代码
kept = starting.ensureActivityConfiguration(changes,
false /* preserveWindow */);
// And we need to make sure at this point that all other activities
// are made visible with the correct configuration.
mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes,
!PRESERVE_WINDOWS);
}
}
return kept;
}
复制代码
可以看到,决定返回值的是 ActivityRecord
的 ensureActivityConfiguration()
方法,并在内部调用了该方法的重载方法,其源码如下:
boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
boolean ignoreStopState) {
final ActivityStack stack = getStack();
// 如果马上就会再次调用updateConfiguration(),则忽略本次修改,交由下次处理,节省时间
if (stack.mConfigWillChange) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check (will change): " + this);
return true;
}
// We don't worry about activities that are finishing.
// 如果当前activity已经finish则忽略
if (finishing) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Configuration doesn't matter in finishing " + this);
stopFreezingScreenLocked(false);
return true;
}
// ...省略一段代码
if (mState == INITIALIZING) {
// No need to relaunch or schedule new config for activity that hasn't been launched
// yet. We do, however, return after applying the config to activity record, so that
// it will use it for launch transaction.
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Skipping config check for initializing activity: " + this);
return true;
}
if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
// Aha, the activity isn't handling the change, so DIE DIE DIE.
configChangeFlags |= changes;
startFreezingScreenLocked(app, globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
if (app == null || app.thread == null) {
// ...省略log代码
// 如果app不在托管状态,则仅销毁当前activity
stack.destroyActivityLocked(this, true, "config");
} else if (mState == PAUSING) {
// ...省略log代码
// 如果当前activity处于PAUSING状态,则标记其需要重启,等到PAUSING后reLaunch
deferRelaunchUntilPaused = true;
preserveWindowOnDeferredRelaunch = preserveWindow;
return true;
} else if (mState == RESUMED) {
// ...省略一段代码
// 如果当前activity处于RESUMED状态,则重启后需要恢复到RESUMED状态
relaunchActivityLocked(true /* andResume */, preserveWindow);
} else {
// ...省略log代码
relaunchActivityLocked(false /* andResume */, preserveWindow);
}
// activity自行处理了configuration changed,无需重启
return false;
}
// Activity可以自己处理配置变更则走这里
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
return true;
}
复制代码
可以看到决定是否重启的关键代码是 shouldRelaunchLocked(changes, mTmpConfig)
。另外一个值得关注的点是 forceNewConfig
变量,其值仅在 ActivityStack.restartPackage()
时为 true
,此时会忽略activity的 configChanges
配置,强制重启activity。 shouldRelaunchLocked
的源码如下:
private boolean shouldRelaunchLocked(int changes, Configuration changesConfig) {
// 获取manifest中配置的configChanges属性
int configChanged = info.getRealConfigChanged();
boolean onlyVrUiModeChanged = onlyVrUiModeChanged(changes, changesConfig);
// Override for apps targeting pre-O sdks
// If a device is in VR mode, and we're transitioning into VR ui mode, add ignore ui mode
// to the config change.
// For O and later, apps will be required to add configChanges="uimode" to their manifest.
if (appInfo.targetSdkVersion < O
&& requestedVrComponent != null
&& onlyVrUiModeChanged) {
configChanged |= CONFIG_UI_MODE;
}
// 关键代码
return (changes&(~configChanged)) != 0;
}
复制代码
(changes&(~configChanged)) != 0
决定了是否ReLaunch当前activity,如果变更的配置在activity自处理的配置列表中,则不会重启。而configChanged正是我们在manifest中配置的configChanges属性。
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
// ...省略一段代码
try {
// ...省略log代码
// 关键代码1
final ClientTransactionItem callbackItem = ActivityRelaunchItem.obtain(pendingResults,
pendingNewIntents, configChangeFlags,
new MergedConfiguration(service.getGlobalConfiguration(),
getMergedOverrideConfiguration()),
preserveWindow);
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(service.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
final ClientTransaction transaction = ClientTransaction.obtain(app.thread, appToken);
transaction.addCallback(callbackItem);
transaction.setLifecycleStateRequest(lifecycleItem);
// 关键代码2
service.getLifecycleManager().scheduleTransaction(transaction);
} catch (RemoteException e) {
if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG_SWITCH, "Relaunch failed", e);
}
// ...省略一段代码
}
复制代码
首先我们看关键代码1: ActivityRelaunchItem.obtain()
。 ActivityRelaunchItem
继承自``,其中在 execute()
中调用了 client.handleRelaunchActivity(mActivityClientRecord, pendingActions)
。
最终 ClientTransaction
的callback的 execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions)
都会被调用,而添加的callback: ActivityRelaunchItem
的 execute
如下:
public void execute(ClientTransactionHandler client, IBinder token,PendingTransactionActions pendingActions) {
if (mActivityClientRecord == null) {
if (DEBUG_ORDER) Slog.d(TAG, "Activity relaunch cancelled");
return;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
client.handleRelaunchActivity(mActivityClientRecord, pendingActions);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
复制代码
ClientTransactionHandler
是一个抽象类,这里实际是通过 ClientTransaction.obtain(app.thread, appToken)
传入的 IApplicationThread
对象,app.thread是在 attachApplication()
方法中设置的,其实现类是 ApplicationThread
,而其内部调用的是 ActivityThread.handleRelaunchActivity()
,该方法在其父类中实现:
void scheduleTransaction(ClientTransaction transaction) {
transaction.preExecute(this);
sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
复制代码
其中 sendMessage()
由 ActivityThread
实现,最终交由H处理:
case EXECUTE_TRANSACTION:
final ClientTransaction transaction = (ClientTransaction) msg.obj;
mTransactionExecutor.execute(transaction);
if (isSystem()) {
transaction.recycle();
}
break;
复制代码
mTransactionExecutor
是 TransactionExecutor
的实例,其 execute()
方法最终会调用 executeCallbacks(transaction)
调用通过 ClientTransaction#addCallback()
方法添加的所有 ClientTransactionItem
实例的 execute()
。最终会调用 ActivityThread.handleRelaunchActivity()
:
public void handleRelaunchActivity(ActivityClientRecord tmp,PendingTransactionActions pendingActions) {
// ...省略一段代码
if (changedConfig != null) {
mCurDefaultDisplayDpi = changedConfig.densityDpi;
updateDefaultDensity();
handleConfigurationChanged(changedConfig, null);
}
ActivityClientRecord r = mActivities.get(tmp.token);
// ...省略一段代码
handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
// ...省略一段代码
}
复制代码
在 handleRelaunchActivityInner()
中,先调用 ActivityThread.handleDestroyActivity()
销毁当前activity,随便调用 ActivityThread.handleLaunchActivity()
重启了activity。我们都知道,Activity有一个回调方法 onRetainNonConfigurationInstance()
,当设备信息变更时,会保存该方法返回的Object,之后可以在重启的Activity中通过 getLastNonConfigurationInstance()
获取该Object。 onRetainNonConfigurationInstance()
并非仅会在发生reLaunchActivity时回调,而是在Activity destoryed时,在 ActivityThread.performDestroyActivity()
中调用 Activity.retainNonConfigurationInstances()
获取的。该方法返回的是 NonConfigurationInstances
,其 activity
属性便是调用 Activity.onRetainNonConfigurationInstance()
获取的。而之所以 getLastNonConfigurationInstance()
能获取到值,是因为在reLaunchActivity中将同一 ActivityRecord
作为参数,传递给了新Activity。该方法是在 ComponentActivity
,已经被重写为final方法,子类如果想保存数据,可以通过 onRetainCustomNonConfigurationInstance
替代,但是官方推荐使用ViewModel组件来替代它,而ViewModel之所以会在设备旋转后恢复,便是通过这种方式保存的。
现在要解决的疑惑是,当变更的配置在activity自处理的配置列表时,activity仅会回调 onConfigurationChanged(Configuration)
,这又是在哪里调用的呢?答案就在 ActivityRecord
的 ensureActivityConfiguration()
方法中。
// Activity可以自己处理配置变更则走这里
if (displayChanged) {
scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
} else {
scheduleConfigurationChanged(newMergedOverrideConfig);
}
复制代码
这两个分支,最终都会调用 ClientTransactionHandler.handleActivityConfigurationChanged()
方法,该方法由 ActivityThread
实现:
public void handleActivityConfigurationChanged(IBinder activityToken,Configuration overrideConfig, int displayId) {
// ...省略一段代码
final boolean movedToDifferentDisplay = displayId != INVALID_DISPLAY
&& displayId != r.activity.getDisplay().getDisplayId();
// ...省略一段代码
if (movedToDifferentDisplay) {
// ...省略一段代码
final Configuration reportedConfig = performConfigurationChangedForActivity(r,
mCompatConfiguration, displayId, true /* movedToDifferentDisplay */);
// ...省略一段代码
} else {
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle activity config changed: "
+ r.activityInfo.name + ", config=" + overrideConfig);
performConfigurationChangedForActivity(r, mCompatConfiguration);
}
// ...省略一段代码
}
复制代码
performConfigurationChangedForActivity()
最终会调用 performActivityConfigurationChanged()
方法,该方法如下:
private Configuration performActivityConfigurationChanged(Activity activity,Configuration newConfig, Configuration amOverrideConfig, int displayId,boolean movedToDifferentDisplay) {
// ...省略一段代码
boolean shouldChangeConfig = false;
if (activity.mCurrentConfig == null) {
shouldChangeConfig = true;
} else {
// If the new config is the same as the config this Activity is already running with and
// the override config also didn't change, then don't bother calling
// onConfigurationChanged.
final int diff = activity.mCurrentConfig.diffPublicOnly(newConfig);
if (diff != 0 || !mResourcesManager.isSameResourcesOverrideConfig(activityToken,
amOverrideConfig)) {
// Always send the task-level config changes. For system-level configuration, if
// this activity doesn't handle any of the config changes, then don't bother
// calling onConfigurationChanged as we're going to destroy it.
// 如果共用配置发生变更
// mUpdatingSystemConfig这里为false所以shouldChangeConfig=true
if (!mUpdatingSystemConfig
|| (~activity.mActivityInfo.getRealConfigChanged() & diff) == 0
|| !REPORT_TO_ACTIVITY) {
shouldChangeConfig = true;
}
}
}
if (!shouldChangeConfig && !movedToDifferentDisplay) {
// Nothing significant, don't proceed with updating and reporting.
return null;
}
// ...省略一段代码
if (shouldChangeConfig) {
activity.mCalled = false;
activity.onConfigurationChanged(configToReport);
if (!activity.mCalled) {
throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
" did not call through to super.onConfigurationChanged()");
}
}
return configToReport;
}
复制代码
最终我们找到了 activity.onConfigurationChanged(configToReport)
调用位置。至此,设备变更是,activity的生命周期调用流程分析完毕。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- PyMiner 开源协议变更为 LGPL,技术变更为 PySide2
- 使用JGit获取变更细节
- 每日获取变更的CVE漏洞
- Raft 成员变更的工程实践
- 生产变更的几点感悟
- 需求变更控制有哪些先进技术?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
零基础学Java Web开发
刘聪 编 / 机械工业出版社 / 2008-1 / 59.00元
《零基础学Java Web开发:JSP+Servlet+Sfruts+Spring+Hibernte》全面讲解Java Web应用开发的编程技术,并详细介绍Java Web开发中各种常用的技术,可作为Java Web开发技术的学习指南。 《零基础学Java Web开发:JSP+Servlet+Sfruts+Spring+Hibernte》共17章,分为3篇,其中第1~12章是基础篇,讲解了......一起来看看 《零基础学Java Web开发》 这本书的介绍吧!