Configuration 变更时Activity的生命周期探究

栏目: IOS · Android · 发布时间: 4年前

内容简介:当设备配置发生变更时,系统会调用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;
    }
复制代码

可以看到,决定返回值的是 ActivityRecordensureActivityConfiguration() 方法,并在内部调用了该方法的重载方法,其源码如下:

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: ActivityRelaunchItemexecute 如下:

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;
复制代码

mTransactionExecutorTransactionExecutor 的实例,其 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) ,这又是在哪里调用的呢?答案就在 ActivityRecordensureActivityConfiguration() 方法中。

// 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的生命周期调用流程分析完毕。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Vim实用技巧

Vim实用技巧

[英] Drew Neil / 杨源、车文隆 / 人民邮电出版社 / 2014-5-1 / 59.00元

vim是一款功能丰富而强大的文本编辑器,其代码补全、编译及错误跳转等方便编程的功能特别丰富,在程序员中得到非常广泛的使用。vim能够大大提高程序员的工作效率。对于vim高手来说,vim能以与思考同步的速度编辑文本。同时,学习和熟练使用vim又有一定的难度。 《vim实用技巧》为那些想要提升自己的程序员编写,阅读本书是熟练地掌握高超的vim技巧的必由之路。全书共21章,包括121个技巧。每一章......一起来看看 《Vim实用技巧》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换