优化系列一:FragmentStatePagerAdapter的notifyDataSetChanged优化方案

FragmentStatePagerAdapter的notifyDataSetChanged优化方案

首先上图:

接下来我们先来分析下布局

优化系列一:FragmentStatePagerAdapter的notifyDataSetChanged优化方案_第1张图片

很容易可以看出是radiogroup+指示器+viewpager+fragment来展示。一开始我按照正常的方式来做,会发现有几个问题。第一:这里有十几个fragment,一进来就会执行十几次网络请求。虽说网络请求都是异步执行,但是这个一是浪费流量,二是总感觉有点不对劲慌慌的,虽然我也做了退出取消网络的方法。第二,每次我点击昨天这个按钮时,notifyDataSetChanged方法好像没起作用。

针对问题一:

我采用fragment的懒加载属性+viewpager的缓存属性,可以得到当fragment页面可见时才执行网络请求。而viewpager的缓存属性,可以防止fragment重复加载。只需要两个标记位即可。需要注意的是setUserVisibleHint方法并不可控,也就意味着setUserVisibleHint方法有可能在view初始化之前执行,那这个时候有那么一丝可能会造成,网络请求成功但是view是null的情况。所以这个地方加入了一个isInit是否view初始化完成的判断。

//fragment实现懒加载的关键代码
public abstract class BaseLazyFragment extends BaseFragment
{
    private static final String TAG = "BaseLazyFragment";
    protected boolean isInit = false;
    protected boolean isGetData = false;

    @Override
    public void doBusiness(Context context)
    {
        if (getUserVisibleHint())
        {
            visibleToPerform();
        }
        isInit = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser)
    {
        super.setUserVisibleHint(isVisibleToUser);
        if (getUserVisibleHint() && isInit && !isGetData)
        {
            visibleToPerform();
        }
    }

    public abstract void visibleToPerform();

}

针对问题二:

notifyDataSetChanged方法中进入源码可以看到会走viewpager的dataSetChanged()方法。

下面看关键代码有关键两步:

1.会调用adapter中的getItemPosition方法;

2.会根据getItemPosition方法返回的int值来进行判断是否重建itemView;

void dataSetChanged() {
    // This method only gets called if our observer is attached, so mAdapter is non-null.

    final int adapterCount = mAdapter.getCount();
    mExpectedAdapterCount = adapterCount;
    boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
            && mItems.size() < adapterCount;
    int newCurrItem = mCurItem;

    boolean isUpdating = false;
    for (int i = 0; i < mItems.size(); i++) {
        final ItemInfo ii = mItems.get(i);
        //1.首先会调用getItemPosition方法
        final int newPos = mAdapter.getItemPosition(ii.object);
        //2.会判断getItemPosition方法的返回值,只有等于PagerAdapter.POSITION_NONE时
        //才会将其销毁并重建
        if (newPos == PagerAdapter.POSITION_UNCHANGED) {
            continue;
        }

        if (newPos == PagerAdapter.POSITION_NONE) {
            mItems.remove(i);
            i--;

            if (!isUpdating) {
                mAdapter.startUpdate(this);
                isUpdating = true;
            }

            mAdapter.destroyItem(this, ii.position, ii.object);
            needPopulate = true;

            if (mCurItem == ii.position) {
                // Keep the current item in the valid range
                newCurrItem = Math.max(0, Math.min(mCurItem, adapterCount - 1));
                needPopulate = true;
            }
            continue;
        }

        if (ii.position != newPos) {
            if (ii.position == mCurItem) {
                // Our current item changed position. Follow it.
                newCurrItem = newPos;
            }

            ii.position = newPos;
            needPopulate = true;
        }
    }

所以我一开始的初步解决方案是重写adapter中的getItemPosition方法如下所示:

@Override
public int getItemPosition(Object object)
{
    return POSITION_NONE;
}

问题貌似已经解决了。但是,等等,为什么我每次点击昨天,今天按钮切换的时候,会有将近1000ms的卡顿!作为一个追求完美的开发者来说,真的有点不能忍。然后马上我就意识到,上面那个方法其实是最差劲的方法,是最无奈的一种方法。因为他把所有的fragment都销毁了,然后进行重建,十几个啊,所以造成了UI界面的卡顿现象。所以必须想出一种优化方法,我想了下,最后我的优化方式如下:

@Override
public int getItemPosition(Object object)
{
    //针对 pageAdapter notifychangeData的优化  无卡顿
    Log.e(TAG, "getItemPosition:" + mPosition);
    RegionOnlineRequestEntity entity = mDatas.get(0);
    if (object instanceof AreaOnlineChildFragment)
    {
        ((AreaOnlineChildFragment) object).setRequestEntity(entity);
        if (((AreaOnlineChildFragment) object).getUserVisibleHint())
        {
            ((AreaOnlineChildFragment) object).visibleToPerform();
        }
    }
    return super.getItemPosition(object);
}

这里面有可以在setRequestEntity做一些请求实体类的数据改变操作和挺关键是isGetData设置为false然后再判断当前fragment是否可见,再进行刷新操作。这样的话,每次刷新就只会刷新一个页面,在我两年多年前买的千元Android机器上都没有卡顿效果哦!

以上。

下篇博客:动手系列一:动手写一个状态布局

你可能感兴趣的:(Android实战开发)