ViewPage中Fragment切换声明周期问题

2019-08-09  本文已影响0人  ae12

记录下博客地址:
https://blog.csdn.net/czhpxl007/article/details/51277319
以及FragmentPageAdapter 和 FragmentStatePageAdapter对Fragment的生命周期的影响
问题:项目中ViewPage的setcurrentItem(itempostion) 切换Fragment的时候,发现fragment的数据没有刷新;然后打印它的生命周期方法,均没有执行。原因是预加载设置: mViewPager.setOffscreenPageLimit(mTabTitles.length - 1);

当你宿主Activity加载时候,你的Fragment会全部被创建加载进来。主要方法调用PagerAdapter的实现类FragmentPagerAdapter or FragmentStatePagerAdapter的instantiateItem()方法;如果你查看源码会发现,这个方法走了FragmetTransation的一系列方法,现在贴一下源码:

FragmentStatePagerAdapter.java
@NonNull
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        Fragment fragment;
        if (this.mFragments.size() > position) {
            fragment = (Fragment)this.mFragments.get(position);
            if (fragment != null) {
                return fragment;
            }
        }

        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        fragment = this.getItem(position);
        if (this.mSavedState.size() > position) {
            SavedState fss = (SavedState)this.mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }

        while(this.mFragments.size() <= position) {
            this.mFragments.add((Object)null);
        }

        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
        this.mFragments.set(position, fragment);
        this.mCurTransaction.add(container.getId(), fragment);
        return fragment;
    }

FragmentPagerAdapter.java里的方法

public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = this.mFragmentManager.beginTransaction();
        }

        long itemId = this.getItemId(position);
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            this.mCurTransaction.attach(fragment);
        } else {
            fragment = this.getItem(position);
            this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
        }

        if (fragment != this.mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

因此,预加载会导致不可见的Fragment一股脑的调用onCreate、onCreateView、onResume等方法,用户只能通一些方法进行识别:Fragment.setUserVisibleHint()。
一张传统fragment生命周期图,每次都忘😭😭😭😭😭😭😭😭


屏幕快照 2019-11-11 下午4.45.48.png

所以,你在调用ViewPage的setcurrentItem(itempostion) 切换Fragment,只会走fragment.setUserVisibleHint()方法,那么又有问题来了:
1.fragment是如何知道自己什么时候是用户可见?
2.setUserVisibleHint() 在上图所示fragment的生命周期的什么位置?
先上结论:
1.ViewPager监听Tab切换事件,每次切换tab都会走setUserVisibleHint()方法;
2.setUserVisibleHint()在所有上面的fragment生命周期 之前调用,无论ViewPager在Activity的哪个生命周期初始化

//每次切换ViewPager的Tab时调用的方法
void populate(int newCurrentItem) {

        mAdapter.startUpdate(this);

        //......
        addNewItem(mCurItem, curIndex);
        // mCurItem 为当前可见Fragment
        // 调用setUserVisibleHint(true)
        mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null); 

        mAdapter.finishUpdate(this);
        //.....

 }


ItemInfo addNewItem(int position, int index) {
        ItemInfo ii = new ItemInfo();
        ii.position = position;
        //初始化fragment, 调用setUserVisibleHint(false)
        ii.object = mAdapter.instantiateItem(this, position);
        ii.widthFactor = mAdapter.getPageWidth(position);
        if (index < 0 || index >= mItems.size()) {
            mItems.add(ii);
        } else {
            mItems.add(index, ii);
        }
        return ii;
}

meworks/support/v4/java/android/support/v4/app/FragmentPagerAdapter.java

    @Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
        //将fragment添加到FragmentManager里面
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        //我们要找的方法在这里
        fragment.setUserVisibleHint(false); 
    }

    return fragment;
}

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment)object;
    if (fragment != mCurrentPrimaryItem) {
        if (mCurrentPrimaryItem != null) {
            mCurrentPrimaryItem.setMenuVisibility(false);
            mCurrentPrimaryItem.setUserVisibleHint(false);
        }
        if (fragment != null) {
            //我们要找的方法在这里
            fragment.setMenuVisibility(true);
            fragment.setUserVisibleHint(true);
        }
        mCurrentPrimaryItem = fragment;
    }
}

所以,这里可以看到,每次setCurrentItem()的时候,都会调用到setCurrentItemInternal{
//...
populate(item);
//...
}
然后poulate()一路调用到 fragment.setUserVisibleHint(true);
接着,看Activity中对fragment的生命周期的管理:

上一篇 下一篇

猜你喜欢

热点阅读