本文基于Android 13源码分析

目的

在Android 系统中,屏幕切换,语言切换,昼夜色切换后,默认情况下会触发Activity的重启,这可能会影响用户体验,如果是导航app,则会影响导航的连贯性,如果app依赖其他的模块如3D模型等,这会导致模型黑屏,重新加载缓慢等体验很差的效果.同时为了保存和恢复状态,还不得不重写onSaveInstanceState和onRestoreInstanceState,非常繁琐.

而onConfigurationChanged回调就是为了解决这类问题的,不会导致Activity销毁和重建.通过一个回调接口来刷新UI回调,我们只需要在回调中手动刷新UI和资源,保证应用使用的连贯性,也简化了开发逻辑

怎么使用

onConfigurationChanged使用时和订阅发布模型类似,Activity向系统订阅自己关心的配置变化, 在AndroidManifest.xml中,加入

        <activity
            android:name=".MainActivity"
            android:exported="true"
            // 向系统订阅昼夜色主题变化,屏幕旋转,多语言切换
            android:configChanges="uiMode|screenSize|orientation|locale"
        >

没有在configChanges中订阅的参数,当系统变更后Activity则会销毁重建.

在Activity中重写onConfigurationChanged函数

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        // newConfig新配置,可以读取屏幕旋转之后的方向,切换之后的语言
    }

在函数中我们可以处理自己的业务逻辑了:

  • 多语言切换:调用代码重新加载string资源并setText更新UI

  • 屏幕旋转:判断屏幕方向,加载不同的布局,定制化刷新UI

源码关键流程分析

总时序图

我们先整理看下系统配置变更到回调通知的整体流程图,然后分步解析其中的关键源码

第一阶段通知Application config变更

sequenceDiagram participant ATMS as ATMS<br>(ActivityTaskManagerService) participant WPC as WPC<br>(WindowProcessController) participant AT as AT<br>(ActivityThread) participant CC as CC<br>(ConfigurationController) ATMS ->> ATMS:updateConfiguration<br>其他进程发起跨进程调用,<br>触发配置变更 note over ATMS:第一阶段<br>通知Application配置变化 ATMS ->> ATMS:updateGlobalConfigurationLocked<br>收集所有正在运行的进程对象<br>WindowProcessController ATMS ->> WPC:onConfigurationChanged<br>遍历交给其对应的<br>WindowProcessController处理 WPC -->> WPC:dispatchConfiguration WPC ->> AT:scheduleTransaction<br>通过持有的IApplicationThread<br>跨进程调用到ActivityThread<br>这里会触发transaction进入<br>ConfigurationChangeItem WPC ->> AT:handleConfigurationChanged <br>由ConfigurationChangeItem调用 AT ->> CC:handleConfigurationChanged<br>委托给 ConfigurationController<br> 处理配置变化,实现职责分离 CC ->> AT:collectComponentCallbacks<br>获取组件集合 AT -->> CC:返回进程中的<br>Application,<br>Service,Provider集合 CC -->> CC:performConfigurationChanged CC -->> CC:调用Applicaiton,Service,Provider<br>的onConfigurationChanged接口,<br>都实现了接口<br>ComponentCallbacks2

第二阶段通知Activity config变更

sequenceDiagram participant ATMS as ATMS<br>(ActivityTaskManagerService) participant WPC as WPC<br>(WindowProcessController) participant AT as AT<br>(ActivityThread) participant AR as ActivityRecord note over ATMS:第二阶段<br>判断Activity是重启<br>还是走onConfigurationChanged ATMS ->> ATMS:ensureConfigAndVisibilityAfterUpdate<br>获取top activity的<br>ActivityRecord ATMS ->> AR:ensureActivityConfiguration alt Activity 不可见 AR -->> ATMS:return end AR -->> AR:shouldRelaunchLocked<br>读取AndroidManifest.xml<br>配置的configChanges alt true AR ->> AT:relaunchActivityLocked<br>通过ActivityRelaunchItem重启Activity else false AR ->> AR:scheduleConfigurationChanged ATMS ->> AT:scheduleTransaction<br>通过跨进程执行到ActivityThread并触发ActivityConfigurationChangeItem AR ->> AT:handleActivityConfigurationChanged<br>在ActivityConfigurationChangeItem执行 AT ->> AT:performConfigurationChangedForActivity AT ->> AT:shouldReportChange AT ->> AT: 触发Activity的<br>onCOnfigurationChanged end

Transaction跨进程流程

Android 9(P)之后逐渐加入了Transaction机制

sequenceDiagram participant CT as CT<br>(ClientTransaction) participant AT as AT<br>(ActivityThread) participant H as H<br>(Handler) participant E as E<br>TransactionExecutor participant CTI as CTI<br>(ClientTransactionItem) note over CT: 外部接口ATMS调用<br>getLifecycleManager().<br>scheduleTransaction(transaction) CT ->> CT: setLifecycleStateRequest<br>param:ClientTransaction CT ->> CT: addCallback<br>param:ClientTransaction CT ->> AT: scheduleTransaction<br>通过持有的mClient对象<br>IApplicationThread发起跨进程通信 AT ->> AT: sendMessage<br>发送消息:EXECUTE_TRANSACTION AT ->> H: sendMessage<br>交给Handler处理 H ->> H: handleMessage<br>EXECUTE_TRANSACTION H ->> E: execute E ->> E: executeCallbacks E ->> E: executeLifecycleState E ->> CT: getCallbacks CT -->> E: 返回注册的List<ClientTransactionItem> E ->> CTI:execute E ->> CTI: postExecute CTI ->> AT: 根据ClientTransactionHandler不同的实现类调用不同的接口<br>如handleActivityConfigurationChanged

将上面三个流程图合并

sequenceDiagram participant ATMS as ATMS<br>(ActivityTaskManagerService) participant WPC as WPC<br>(WindowProcessController) participant CCI as CCI<br>(ConfigurationChangeItem) participant AT as AT<br>(ActivityThread) participant CC as CC<br>(ConfigurationController) participant AR as ActivityRecord autonumber ATMS ->> ATMS:updateConfiguration<br>其他进程发起跨进程调用,<br>触发配置变更 note over ATMS:第一阶段<br>通知Application配置变化 ATMS ->> ATMS:updateGlobalConfigurationLocked<br>收集所有正在运行的进程对象<br>WindowProcessController ATMS ->> WPC:onConfigurationChanged<br>遍历交给其对应的<br>WindowProcessController处理 WPC -->> WPC:dispatchConfiguration WPC ->> AT:scheduleTransaction<br>通过持有的IApplicationThread跨进程调用到ActivityThread AT ->> CCI:execute()<br>通过Handler发送消息<br>EXECUTE_TRANSACTION触发调用 CCI ->> AT:handleConfigurationChanged<br>配置变更的具体逻辑<br>由ActivityThread实现 AT ->> CC:handleConfigurationChanged<br>委托给 ConfigurationController<br> 处理配置变化,实现职责分离 CC ->> AT:collectComponentCallbacks<br>获取组件集合 AT -->> CC:返回进程中的<br>Application,<br>Service,Provider集合 CC -->> CC:performConfigurationChanged CC -->> CC:调用Applicaiton,Service,Provider<br>的onConfigurationChanged接口,<br>都实现了接口<br>ComponentCallbacks2 note over ATMS:第二阶段<br>判断Activity是重启<br>还是走onConfigurationChanged ATMS ->> ATMS:ensureConfigAndVisibilityAfterUpdate<br>获取top activity的<br>ActivityRecord ATMS ->> AR:ensureActivityConfiguration alt Activity 不可见 AR -->> ATMS:return end AR -->> AR:shouldRelaunchLocked<br>读取AndroidManifest.xml<br>配置的configChanges alt true AR ->> AT:relaunchActivityLocked<br>通过ActivityRelaunchItem重启Activity else false AR ->> AR:scheduleConfigurationChanged ATMS ->> AT:scheduleTransaction<br>通过跨进程执行到ActivityThread并触发ActivityConfigurationChangeItem AR ->> AT:handleActivityConfigurationChanged<br>在ActivityConfigurationChangeItem执行 AT ->> AT:performConfigurationChangedForActivity AT ->> AT:shouldReportChange AT ->> AT: 触发Activity的<br>onCOnfigurationChanged end

配置变更

先看看触发onConfigurationChange的场景,这里例举两种情况

屏幕旋转

屏幕旋转主要类是DisplayRotation,接着通过DisplayContent#sendNewConfiguration产生回调

base/services/core/java/com/android/server/wm/DisplayContent.java

    void sendNewConfiguration() {
        ...
        final boolean configUpdated = updateDisplayOverrideConfigurationLocked();
        if (configUpdated) {
            return;
        }
        ...
   }

    boolean updateDisplayOverrideConfigurationLocked() {
        ...
        Configuration values = new Configuration();
        computeScreenConfiguration(values);
        updateDisplayOverrideConfigurationLocked(values, null /* starting */,
                false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
        return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
    }

updateDisplayOverrideConfigurationLocked中,新建了一个Configuration,然后通过computeScreenConfiguration计算出最新的屏幕配置信息,然后调用updateDisplayOverrideConfigurationLocked

base/services/core/java/com/android/server/wm/DisplayContent.java

 boolean updateDisplayOverrideConfigurationLocked(Configuration values,
            ActivityRecord starting, boolean deferResume,
            ActivityTaskManagerService.UpdateConfigurationResult result) {

        int changes = 0;
        boolean kept = true;
        try {
            if (values != null) {
                if (mDisplayId == DEFAULT_DISPLAY) {
                    // 1.更新application全局config
                    changes = mAtmService.updateGlobalConfigurationLocked(values,
                            false /* initLocale */, false /* persistent */,
                            UserHandle.USER_NULL /* userId */);
                } else {
                    changes = performDisplayOverrideConfigUpdate(values);
                }
            }

            if (!deferResume) {
                // 2.更新activity config
                kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
            }
        } finally {
            mAtmService.continueWindowLayout();
        }

        return kept;
    }

1.这里也调用了ATMS的updateGlobalConfigurationLocked方法,调用流程同时序图中步骤2

2.后调用了ATMS的ensureConfigAndVisibilityAfterUpdate方法,时序同步骤13

昼夜色切换

在SystemUI 下拉栏中切换黑暗模式,调用setNightModeActivated触发configChange

base/services/core/java/com/android/server/UiModeManagerService.java

        @Override
        public boolean setNightModeActivated(boolean active) {
            return setNightModeActivatedForModeInternal(mNightModeCustomType, active);
        }

        private boolean setNightModeActivatedForModeInternal(int modeCustomType, boolean active) {
            ...
            synchronized (mLock) {
                final long ident = Binder.clearCallingIdentity();
                try {
                    ...
                    // 更新config对象
                    updateConfigurationLocked();
                    // 调用ATMS更新配置
                    applyConfigurationExternallyLocked();
                    persistNightMode(mCurrentUser);
                    return true;
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        }

updateConfigurationLocked函数主要是更新mConfiguration.uiMode字段

applyConfigurationExternallyLocked函数调用ActivityTaskManager.getService().updateConfiguration(mConfiguration)发起跨进程调用,进入ATMS流程,即时序图的步骤1

更新Application全局配置

入口是ATMS的updateGlobalConfigurationLocked

    int updateGlobalConfigurationLocked(@NonNull Configuration values, boolean initLocale,
            boolean persistent, int userId) {
        // 1.读取原始Configuration
        mTempConfig.setTo(getGlobalConfiguration());
        ...
        // 2.根据pid读取所有正在运行的进程
        SparseArray<WindowProcessController> pidMap = mProcessMap.getPidMap();
        for (int i = pidMap.size() - 1; i >= 0; i--) {
            final int pid = pidMap.keyAt(i);
            final WindowProcessController app = pidMap.get(pid);
            ProtoLog.v(WM_DEBUG_CONFIGURATION, "Update process config of %s to new "
                    + "config %s", app.mName, mTempConfig);
            // 3
            app.onConfigurationChanged(mTempConfig);
        }

        // 4.广播
        final Message msg = PooledLambda.obtainMessage(
                ActivityManagerInternal::broadcastGlobalConfigurationChanged,
                mAmInternal, changes, initLocale);
        mH.sendMessage(msg);

        // Update stored global config and notify everyone about the change.
        mRootWindowContainer.onConfigurationChanged(mTempConfig);

        return changes;
    }
  1. 这里的getGlobalConfiguration()获取的是原始Configuration,保存到mTempConfig

  2. 读取所有正在运行的process, 存在mProcessMap对象里边

  3. 遍历所有进程,然后将Configuration交给其WindowProcessController 处理,WindowProcessController 是ConfigurationContainer的子类

  4. 发了一个系统广播

com.android.server.am.ActivityManagerService.LocalService

public static final String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";

        @Override
        public void broadcastGlobalConfigurationChanged(int changes, boolean initLocale) {
            synchronized (ActivityManagerService.this) {
                Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                        | Intent.FLAG_RECEIVER_REPLACE_PENDING
                        | Intent.FLAG_RECEIVER_FOREGROUND
                        | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
                broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
                        null, null, OP_NONE, null, false, false, MY_PID, SYSTEM_UID,
                        Binder.getCallingUid(), Binder.getCallingPid(), UserHandle.USER_ALL);
                ...
        }

WindowProcessController中的跨进程调用

通过ApplicationThread 和应用进程交互,接下来就交给App进程处理了

base/services/core/java/com/android/server/wm/WindowProcessController.java

    void dispatchConfiguration(Configuration config) {
        mHasPendingConfigurationChange = false;
        ...
        try {
            config.seq = mAtm.increaseConfigurationSeqLocked();
            //时序步骤5
            mAtm.getLifecycleManager().scheduleTransaction(mThread,
                    ConfigurationChangeItem.obtain(config));
            setLastReportedConfiguration(config);
        } catch (Exception e) {
            Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e);
        }
    }

跨进程对象ApplicationThread

ATMS跨进程调用到app,跨进程传输的binder对象是mThread(接口IApplicationThread),实现类是ActivityThread中的私有类private class ApplicationThread extends IApplicationThread.Stub

这个binder对象也是通过IActivityManager binder接口从ActivityThread传递到ATMS的

base/core/java/android/app/ActivityThread.java

    @UnsupportedAppUsage
    private void attach(boolean system, long startSeq) {
        sCurrentActivityThread = this;
        mConfigurationController = new ConfigurationController(this);
        mSystemThread = system;
        if (!system) {
            android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                                                    UserHandle.myUserId());
            RuntimeInit.setApplicationObject(mAppThread.asBinder());
            final IActivityManager mgr = ActivityManager.getService();
            try {
                // 将跨进程接口传递到AMS
                mgr.attachApplication(mAppThread, startSeq);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
    ...
    }

ConfigurationChangeItem

时序步骤5发起了ConfigurationChangeItem的一个transaction,此时已经是在应用进程执行任务了,ActivityThread中有一个Handler专门用于处理各种事件,handler收到EXECUTE_TRANSACTION消息后开始执行execute函数,对应时序步骤6

base/core/java/android/app/servertransaction/ConfigurationChangeItem.java

public class ConfigurationChangeItem extends ClientTransactionItem {

    private Configuration mConfiguration;

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        client.handleConfigurationChanged(mConfiguration);
    }
}

时序步骤7在ActivityThread中,handleConfigurationChanged直接委托给ConfigurationController处理,实现职责分离

base/core/java/android/app/ActivityThread.java

public void handleConfigurationChanged(Configuration config) {
    mConfigurationController.handleConfigurationChanged(config);
}

新配置信息分发到Application

ConfigurationController会收集进程中的Application,Provider,Service组件(步骤9),然后触发其onConfigurationChanged回调

void handleConfigurationChanged(@Nullable Configuration config,
        @Nullable CompatibilityInfo compat) {
        ...
        // 收集组件
        final ArrayList<ComponentCallbacks2> callbacks =
                mActivityThread.collectComponentCallbacks(false /* includeUiContexts */);
        
        freeTextLayoutCachesIfNeeded(configDiff);

        if (callbacks != null) {
            final int size = callbacks.size();
            for (int i = 0; i < size; i++) {
                ComponentCallbacks2 cb = callbacks.get(i);
                if (!equivalent) {
                    performConfigurationChanged(cb, config);
                }
            }
        }
    }

参数false表示不包含Activity, Activity涉及到前后台切换configChanges配置,在第二阶段处理,performConfigurationChanged调用函数cb.onConfigurationChanged(configToReport);

更新Activity配置

第二阶段是处理Activity的变更,ATMS入口函数是ensureConfigAndVisibilityAfterUpdate时序13

boolean ensureConfigAndVisibilityAfterUpdate(ActivityRecord starting, int changes) {
    boolean kept = true;
    final Task mainRootTask = mRootWindowContainer.getTopDisplayFocusedRootTask();
    // mainRootTask is null during startup.
    if (mainRootTask != 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 = mainRootTask.topRunningActivity();
        }

        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.
            mRootWindowContainer.ensureActivitiesVisible(starting, changes,
                    !PRESERVE_WINDOWS);
        }
    }

    return kept;
}

首先会获取top activity,然后交给ActivityRecord处理ensureActivityConfiguration

判断可见性

在ensureActivityConfiguration中,首先判断了Activity的可见性,以及生命周期的状态,下边这些状态都不会继续分发配置变更,比如Stop,Destroy,Finish,以及不可见(不只是onPaused状态,内部通过TASK_FRAGMENT_VISIBILITY_VISIBLE来判断,比如锁屏遮挡等)

boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
        boolean ignoreVisibility) {
    ...
    if (finishing) {
        return true;
    }
    if (isState(DESTROYED)) {
        return true;
    }
    if (!ignoreVisibility && (mState == STOPPING || mState == STOPPED || !shouldBeVisible())) {
        return true;
    }
...
    if (mState == INITIALIZING) {
        return true;
    }
...
}

决策是否重启

判断完Activity可见性之后,接着判断是否需要重启,函数shouldRelaunchLocked会读取Manifest.xml配置的属性值

boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
        boolean ignoreVisibility) {
        final Task rootTask = getRootTask();
        ...
        if (shouldRelaunchLocked(changes, mTmpConfig) || forceNewConfig) {
           
            if (mState == 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.
                deferRelaunchUntilPaused = true;
                preserveWindowOnDeferredRelaunch = preserveWindow;
                return true;
            } else {
                relaunchActivityLocked(preserveWindow);
            }

            return false;
        }
...
}

在重启的场景下,如果是Pausing状态,重启则会延迟,否则的话直接重启,Activity对象销毁重建,生命周期先onDestroy然后onCreate

onConfigurationChange回调

不满足重启条件,则进入scheduleConfigurationChanged函数

boolean ensureActivityConfiguration(int globalChanges, boolean preserveWindow,
        boolean ignoreVisibility) {
        final Task rootTask = getRootTask();
    ...
    if (displayChanged) {
        scheduleActivityMovedToDisplay(newDisplayId, newMergedOverrideConfig);
    } else {
        // 走onConfigurationChanged
        scheduleConfigurationChanged(newMergedOverrideConfig);
    }
    stopFreezingScreenLocked(false);

    return true;
}

    private void scheduleConfigurationChanged(Configuration config) {
        
        try {
            //跨进程进入应用进程
            mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), token,
                    ActivityConfigurationChangeItem.obtain(config));
        } catch (RemoteException e) {
            // If process died, whatever.
        }
    }

进入ActivityThread之后,触发的函数是handleActivityConfigurationChanged, 触发的流程和上面的ConfigurationChangeItem逻辑一样

ActivityThread中Configuration处理

时序图
sequenceDiagram participant ACI as ACI<br>(ActivityConfigurationChangeItem) participant AT as AT<br>(ActivityThread) participant A as A<br>(Activity) participant VR as VR<br>(ViewRootImpl) ACI ->> ACI: execute()<br>开始执行transaction ACI ->> AT: handleActivityConfigurationChanged<br>传入ActivityClientRecord,<br>从ActivityThread获取的 AT ->> AT: performConfigurationChangedForActivity AT ->> AT: performActivityConfigurationChanged<br>从ActivityClientRecord读取当前的Activity对象 AT ->> A: onConfigurationChanged<br>应用重写业务逻辑 A ->> A:通知Fragment更新config AT ->> VR:updateConfiguration 通知View config变更
Activity Configuration transaction

base/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java

    @Override
    public void execute(ClientTransactionHandler client, ActivityClientRecord r,
            PendingTransactionActions pendingActions) {
        client.handleActivityConfigurationChanged(r, mConfiguration, INVALID_DISPLAY);
    }

ClientTransactionHandler 的子类是ActivityThread

handleActivityConfigurationChanged实现如下

base/core/java/android/app/ActivityThread.java

@Override
public void handleActivityConfigurationChanged(ActivityClientRecord r,
        @NonNull Configuration overrideConfig, int displayId) {
        ...
        // Perform updates.
        r.overrideConfig = overrideConfig;
        final ViewRootImpl viewRoot = r.activity.mDecor != null
            ? r.activity.mDecor.getViewRootImpl() : null;

        // 1 通知activity config changed
        final Configuration reportedConfig = performConfigurationChangedForActivity(r,
                mConfigurationController.getCompatConfiguration(),
                movedToDifferentDisplay ? displayId : r.activity.getDisplayId());
        // Notify the ViewRootImpl instance about configuration changes. It may have initiated this
        // update to make sure that resources are updated before updating itself.
        if (viewRoot != null) {
            if (movedToDifferentDisplay) {
                viewRoot.onMovedToDisplay(displayId, reportedConfig);
            }
            // 2.通知View config changed
            viewRoot.updateConfiguration(displayId);
        }
        mSomeActivitiesChanged = true;
    }

新的config会更新到 r.overrideConfig变量,接下来分别通知activity和view更新config

通知Activity config变更

base/core/java/android/app/ActivityThread.java

private Configuration performConfigurationChangedForActivity(ActivityClientRecord r,
        Configuration newBaseConfig, int displayId) {
    r.tmpConfig.setTo(newBaseConfig);
    if (r.overrideConfig != null) {
        r.tmpConfig.updateFrom(r.overrideConfig);
    }
    final Configuration reportedConfig = performActivityConfigurationChanged(r.activity,
            r.tmpConfig, r.overrideConfig, displayId);
    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
    return reportedConfig;
}

在performConfigurationChangedForActivity函数里调用了performActivityConfigurationChanged

base/core/java/android/app/ActivityThread.java

private Configuration performActivityConfigurationChanged(Activity activity,
        Configuration newConfig, Configuration amOverrideConfig, int displayId) {
        ...
        final Configuration finalOverrideConfig = createNewConfigAndUpdateIfNotNull(
                amOverrideConfig, contextThemeWrapperOverrideConfig);
        mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId);

        activity.mConfigChangeFlags = 0;
        activity.mCurrentConfig = new Configuration(newConfig);

        // Apply the ContextThemeWrapper override if necessary.
        // NOTE: Make sure the configurations are not modified, as they are treated as immutable
        // in many places.
        // 1.onConfigurationChanged参数传入的Configuration
        final Configuration configToReport = createNewConfigAndUpdateIfNotNull(newConfig,
                contextThemeWrapperOverrideConfig);

        if (movedToDifferentDisplay) {
            activity.dispatchMovedToDisplay(displayId, configToReport);
        }

        if (shouldReportChange) {
            activity.mCalled = false;
            activity.onConfigurationChanged(configToReport);
            // 2
            if (!activity.mCalled) {
                throw new SuperNotCalledException("Activity " + activity.getLocalClassName() +
                                " did not call through to super.onConfigurationChanged()");
            }
        }

        return configToReport;
    }
  1. 这里可以看到传入onConfigurationChanged的config参数的创建

  2. 重写的onConfigurationChanged必须调用super函数,否则会抛异常,Activity中会将将标记置为mCalled = true;

通知Fragment config变更

在Activity中将config 变化通知到所有fragment

base/core/java/android/app/Activity.java

public void onConfigurationChanged(@NonNull Configuration newConfig) {
    mCalled = true;
    mFragments.dispatchConfigurationChanged(newConfig);
}
通知View config变更

在ActivityThread 中通知Activity配置改变之后立即调用 viewRoot.updateConfiguration(displayId);通知View配置变更

base/core/java/android/view/ViewRootImpl.java

public void updateConfiguration(int newDisplayId) {
    if (mView == null) {
        return;
    }
        // Handle configuration change.
        if (mForceNextConfigUpdate || mLastConfigurationFromResources.diff(config) != 0) {
            ...
            //1
            mView.dispatchConfigurationChanged(config);
            mForceNextWindowRelayout = true;
            requestLayout();
        }

        updateForceDarkMode();
    }
  1. 分发到View#onConfigurationChanged函数,所以自定义函数中也可以监听config变化来刷新UI

后台切前台

根据时序13的条件,后台Activity不会继续分发configuration,也不会重启,只有当Activity切换到前台时才会触发配置更新的回调

时序图

sequenceDiagram participant TF as TF<br>(TaskFragment) participant RC as RC<br>(RootWindowContainer) participant D as D<br>(DisplayContent) participant ATMS as ATMS TF ->> TF: resumeTopActivity TF ->> TF: setState这里通过<br>ActivityRecord然后又回到<br>TaskFragment函数调用 TF ->> TF: onActivityStateChanged TF ->> ATMS: scheduleTransaction 发起transaction: TopResumedActivityChangeItem TF -->> TF: shouldBeVisible alt shouldBeVisible == true TF ->> RC: ensureVisibilityAndConfig end RC ->> D: updateDisplay<br>OverrideConfigurationLocked<br> D ->> ATMS: updateGlobalConfigurationLocked<br>更新Application config D ->> ATMS: ensureConfigAndVisibilityAfterUpdate<br>更新Activity config TF ->> ATMS: scheduleTransaction 发起transaction:ResumeActivityItem<br>接下来就会走onResume生命周期

TaskFragment中的resumeTopActivity

当Activity回到前台的时候Task会触发TaskFragment的resumeTopActivity函数,触发onConfigurationChanged的时机是在TopResumedActivityChangeItem之后,ResumeActivityItem之前

base/services/core/java/com/android/server/wm/TaskFragment.java

final boolean resumeTopActivity(ActivityRecord prev, ActivityOptions options,
        boolean deferPause) {
    ...
            next.setState(RESUMED, "resumeTopActivity");
            if (shouldBeVisible(next)) {

                notUpdated = !mRootWindowContainer.ensureVisibilityAndConfig(next, getDisplayId(),
                        true /* markFrozenIfConfigChanged */, false /* deferResume */);
            }
    ...
            try {
                final ClientTransaction transaction =
                        ClientTransaction.obtain(next.app.getThread(), next.token);
                // Deliver all pending results.
                ArrayList<ResultInfo> a = next.results;

                next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
                next.abortAndClearOptionsAnimation();
                transaction.setLifecycleStateRequest(
                        ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                dc.isNextTransitionForward()));
                mAtmService.getLifecycleManager().scheduleTransaction(transaction);

                ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Resumed %s", next);
            } catch (Exception e) {
            }
}

分发到DisplayContent

TaskFragment调用ensureVisibilityAndConfig交给RootWindowContainer处理

boolean ensureVisibilityAndConfig(ActivityRecord starting, int displayId,
        boolean markFrozenIfConfigChanged, boolean deferResume) {
        ...
        // Force-update the orientation from the WindowManager, since we need the true configuration
        // to send to the client now.
        final DisplayContent displayContent = getDisplayContent(displayId); 
        ...
        if (displayContent != null) {
            // Update the configuration of the activities on the display.
            return displayContent.updateDisplayOverrideConfigurationLocked(config, starting,
                    deferResume, null /* result */);
        } else {
            return true;
        }

在RootWindowContainer中调用displayContent的updateDisplayOverrideConfigurationLocked函数处理config变更,这个函数的调用iu成在上面屏幕旋转的逻辑中分析过了,接下去的流程就会走 总时序图中的时序2,之后的逻辑就是重复上面分析过的ATMS处理流程了

春风花气馥,秋月寒江湛