Configuration 變動時Activity的生命週期探究

當設備配置發生變動時,系統會調用AMS的updateConfiguration()方法,來通知AMS處理configuration changed事件,updateConfiguration()源碼以下:java

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()的源碼以下:app

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()方法,並在內部調用了該方法的重載方法,其源碼以下:ide

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的源碼以下:ui

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屬性。this

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以下:lua

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(),該方法在其父類中實現:spa

void scheduleTransaction(ClientTransaction transaction) {
    transaction.preExecute(this);
    sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
}
複製代碼

其中sendMessage()ActivityThread實現,最終交由H處理:rest

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()code

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()方法中。orm

// 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的生命週期調用流程分析完畢。 #Fragment#setRetainInstance 衆所周知,經過調用Fragment#setRetainInstance(true),咱們能夠在因設配配置發生變動而重建時保留Fragment而不是銷燬重建。那麼這一效果又是如何實現的呢? 咱們先來看看Fragment#setRetainInstance()

public void setRetainInstance(boolean retain) {
        mRetainInstance = retain;
        if (mFragmentManager != null) {
            if (retain) {
                mFragmentManager.addRetainedFragment(this);
            } else {
                mFragmentManager.removeRetainedFragment(this);
            }
        } else {
            mRetainInstanceChangedWhileDetached = true;
        }
    }
複製代碼

繼續追蹤發現,Fragment實例被添加到了FragmentManagerViewModel.mRetainedFragments,這是一個HashSet類型的變量,覺得這同一僅容許被添加一次。那麼這些Fragment是在什麼時候保存的呢?想一想以前ViewModel的保存,沒錯就是Activity#retainNonConfigurationInstances()

NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        // 保存 FragmentManagerNonConfig
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        // 賦值給封裝變量
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }
複製代碼

咱們追蹤mFragments.retainNestedNonConfig()看下去,發現調用流程以下: FragmentController#retainNestedNonConfig() -> FragmentManagerImpl#retainNonConfig() ->FragmentManagerViewModel#getSnapshot(), FragmentManagerViewModel#getSnapshot()方法以下:

FragmentManagerNonConfig getSnapshot() {
        if (mRetainedFragments.isEmpty() && mChildNonConfigs.isEmpty()
                && mViewModelStores.isEmpty()) {
            return null;
        }
        HashMap<String, FragmentManagerNonConfig> childNonConfigs = new HashMap<>();
        for (Map.Entry<String, FragmentManagerViewModel> entry : mChildNonConfigs.entrySet()) {
            FragmentManagerNonConfig childNonConfig = entry.getValue().getSnapshot();
            if (childNonConfig != null) {
                childNonConfigs.put(entry.getKey(), childNonConfig);
            }
        }

        mHasSavedSnapshot = true;
        if (mRetainedFragments.isEmpty() && childNonConfigs.isEmpty()
                && mViewModelStores.isEmpty()) {
            return null;
        }
        return new FragmentManagerNonConfig(
                new ArrayList<>(mRetainedFragments),
                childNonConfigs,
                new HashMap<>(mViewModelStores));
    }
複製代碼

能夠看到,若是咱們設置了保留Fragment,那麼被保留的Fragment最終都會保存在FragmentManagerNonConfig變量中,從而被Activity#retainNonConfigurationInstances()所保存,以後做爲參數傳給reluanch的Activity,至此Fragment保存完畢。 下面咱們來看看保留的Fragment是如何被恢復的。 經過閱讀Activity#onCreate()方法咱們發現,若是Activity.mLastNonConfigurationInstances不爲null,即設備配置發生變動保存了相關數據,那麼將會對Fragment進行狀態恢復。Fragment的狀態恢復的關鍵代碼是mFragments.restoreAllState(),這裏的mFragmentsFragmentController的實例,該方法的內部最終調用了FragmentManagerImpl#restoreSaveState(),咱們來看看該方法的實現:

void restoreSaveState(Parcelable state) {
        // ...省略一段代碼
        // First re-attach any non-config instances we are retaining back
        // to their saved state, so we don't try to instantiate them again.
        // 遍歷保留的Fragments
        for (Fragment f : mNonConfig.getRetainedFragments()) {
            if (DEBUG) Log.v(TAG, "restoreSaveState: re-attaching retained " + f);
            FragmentState fs = null;
            // 獲取以前處於Active狀態的Fragments狀態
            for (FragmentState fragmentState : fms.mActive) {
                if (fragmentState.mWho.equals(f.mWho)) {
                    fs = fragmentState;
                    break;
                }
            }
            // 未獲取到狀態直接變動生命週期
            if (fs == null) {
                if (DEBUG) {
                    Log.v(TAG, "Discarding retained Fragment " + f
                            + " that was not found in the set of active Fragments " + fms.mActive);
                }
                // We need to ensure that onDestroy and any other clean up is done
                // so move the Fragment up to CREATED, then mark it as being removed, then
                // destroy it.
                moveToState(f, Fragment.CREATED, 0, 0, false);
                f.mRemoving = true;
                moveToState(f, Fragment.INITIALIZING, 0, 0, false);
                continue;
            }
            // 恢復Fragment的狀態
            fs.mInstance = f;
            f.mSavedViewState = null;
            f.mBackStackNesting = 0;
            f.mInLayout = false;
            f.mAdded = false;
            f.mTargetWho = f.mTarget != null ? f.mTarget.mWho : null;
            f.mTarget = null;
            if (fs.mSavedFragmentState != null) {
                fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
                f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
                        FragmentManagerImpl.VIEW_STATE_TAG);
                f.mSavedFragmentState = fs.mSavedFragmentState;
            }
        }
        // ...省略一段代碼
    }
複製代碼

至此保留的Fragments在的狀態恢復完成。

相關文章
相關標籤/搜索