2025年5月

参数root
当参数root为null时,attachToRoot参数无效。且生成的view会丢失布局文件中的LayoutParams
当参数root不为null时,生成的view会根据root类型来保留布局文件中对应的LayoutParams

参数attachToRoot
attachToRoot为true时,返回的view就是root
attachToRoot为false时,返回的view是跟布局生成的view

问题:一个viewpager2使用了FragmentStateAdapter,当系统配置发生变化(切换横竖屏、切换深色模式、切换语言),会多出fragment实例,每切一次就会多一次。查看日志每次切换后获取fragment的时候,都会重新走FragmentStateAdapter的createFragment。而之前已经生成的fragment都会重建。所以造成上面的现象和问题。

原因:这个viewpager2是动态通过代码new创建的,改成直接写在xml布局中就正常了(一般写在xml中都会加一个id的)

根因:只有绑定id的viewpager2才能正常同步fragment。从xml加载的也需要加id才能。代码动态创建的,设置id后也能正确。

private void ensureFragment(int position) {
    long itemId = getItemId(position);
    if (!mFragments.containsKey(itemId)) {
        // TODO(133419201): check if a Fragment provided here is a new Fragment
        Fragment newFragment = createFragment(position);
        newFragment.setInitialSavedState(mSavedStates.get(itemId));
        mFragments.put(itemId, newFragment);
    }
}

FragmentStateAdapter源码显示,只有mFragments不包含的时候,才会从createFragment拿

@Override
public final void restoreState(@NonNull Parcelable savedState) {
    if (!mSavedStates.isEmpty() || !mFragments.isEmpty()) {
        throw new IllegalStateException(
                "Expected the adapter to be 'fresh' while restoring state.");
    }

    Bundle bundle = (Bundle) savedState;
    if (bundle.getClassLoader() == null) {
        /** TODO(b/133752041): pass the class loader from {@link ViewPager2.SavedState } */
        bundle.setClassLoader(getClass().getClassLoader());
    }

    for (String key : bundle.keySet()) {
        if (isValidKey(key, KEY_PREFIX_FRAGMENT)) {
            long itemId = parseIdFromKey(key, KEY_PREFIX_FRAGMENT);
            Fragment fragment = mFragmentManager.getFragment(bundle, key);
            mFragments.put(itemId, fragment);
            continue;
        }

        if (isValidKey(key, KEY_PREFIX_STATE)) {
            long itemId = parseIdFromKey(key, KEY_PREFIX_STATE);
            Fragment.SavedState state = bundle.getParcelable(key);
            if (containsItem(itemId)) {
                mSavedStates.put(itemId, state);
            }
            continue;
        }

        throw new IllegalArgumentException("Unexpected key in savedState: " + key);
    }

    if (!mFragments.isEmpty()) {
        mHasStaleFragments = true;
        mIsInGracePeriod = true;
        gcFragments();
        scheduleGracePeriodEnd();
    }
}

然后可以看见除了ensureFragment,只有在restoreState这个地方mFragments才会存fragment。关键是要从savedState拿到fragment。

继续跟源码可以发现adapter的restoreState方法是在viewpager2的restorePendingState方法中被调用的,其中传递的参数是mPendingAdapterState,继续看看mPendingAdapterState哪儿来的

@Override
protected void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState ss = (SavedState) state;
    super.onRestoreInstanceState(ss.getSuperState());
    mPendingCurrentItem = ss.mCurrentItem;
    mPendingAdapterState = ss.mAdapterState;
}

mPendingAdapterState来自于viewpager2的onRestoreInstanceState。

protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
    if (mID != NO_ID) {
        Parcelable state = container.get(mID);
        if (state != null) {
            // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
            // + ": " + state);
            mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
            onRestoreInstanceState(state);
            if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
                throw new IllegalStateException(
                        "Derived class did not call super.onRestoreInstanceState()");
            }
        }
    }
}

可以看见只有当有id的时候才会执行onRestoreInstanceState