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

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

内容简介:当设备配置发生变更时,系统会调用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的生命周期调用流程分析完毕。


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

查看所有标签

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

Rails 5敏捷开发

Rails 5敏捷开发

[美] Sam Ruby、[美] Dave Thomas、[美] David Heinemeier Hansson / 安道、叶炜、大疆Ruby技术团队 / 华中科技大学出版社 / 2017-12-30 / 115.00

本书以讲解“购书网站”案例为主线,逐步介绍Rails的内置功能。全书分为3部分,第一部分介绍Rails的安装、应用程序验证、Rails框架的体系结构,以及Ruby语言知识;第二部分用迭代方式构建应用程序,然后依据敏捷开发模式开展测试,最后用Capistrano完成部署;第三部分补充日常实用的开发知识。本书既有直观的示例,又有深入的分析,同时涵盖了Web开发各方面的知识,堪称一部内容全面而又深入浅出......一起来看看 《Rails 5敏捷开发》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具