ViewPager如何通过预加载机制提高滑动性能,以及如何屏蔽预加载策略的方法

开发 前端
调用instantiateItem来创建object,在FragmentPagerAdapter的instantiateItem这个方法中,创建的是Fragment,即缓存的就是Fragment,Fragment在创建时,会有UI操作,网络操作,在还未可见的时候初始化Fragment非常消耗性能,所以应该懒加载的方式来加载,不去缓存Fragment。

ViewPager默认具有预加载机制,会预先加载当前页面前后的一定数量的页面,以便在滑动时能够迅速显示新页面。预加载的数量可以通过setOffscreenPageLimit(int limit)方法来设置,但最小值通常为1,意味着前后各预加载一个页面。

public void setOffscreenPageLimit(int limit) {
    if (limit < 1) {
        Log.w("ViewPager", "Requested offscreen page limit " + limit + " too small; defaulting to " + 1);
        limit = 1;
    }

    if (limit != this.mOffscreenPageLimit) {
        this.mOffscreenPageLimit = limit;
        this.populate();
    }
}

下面看看预加载的是什么,在预加载数量更新的时候,执行polulate()方法。

void populate(int newCurrentItem) {
    ViewPager.ItemInfo oldCurInfo = null;
    if (this.mCurItem != newCurrentItem) {
        oldCurInfo = this.infoForPosition(this.mCurItem);
        this.mCurItem = newCurrentItem;
    }

    if (this.mAdapter == null) {
        this.sortChildDrawingOrder();
    } else if (this.mPopulatePending) {
        this.sortChildDrawingOrder();
    } else if (this.getWindowToken() != null) {
        this.mAdapter.startUpdate(this);//被弃用了
        int pageLimit = this.mOffscreenPageLimit;//当前的缓存页面个数
        int startPos = Math.max(0, this.mCurItem - pageLimit);//计算缓存的开始位置
        int N = this.mAdapter.getCount();//adapter的子内容的数量
        int endPos = Math.min(N - 1, this.mCurItem + pageLimit);//计算缓存的结束位置
        if (N != this.mExpectedAdapterCount) {
            String resName;
            try {
                resName = this.getResources().getResourceName(this.getId());
            } catch (NotFoundException var17) {
                resName = Integer.toHexString(this.getId());
            }

            throw new IllegalStateException("The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: " + this.mExpectedAdapterCount + ", found: " + N + " Pager id: " + resName + " Pager class: " + this.getClass() + " Problematic adapter: " + this.mAdapter.getClass());
        } else {
            int curIndex = true;
            ViewPager.ItemInfo curItem = null;
            //开始去找 ViewPager.ItemInfo
            int curIndex;
            for(curIndex = 0; curIndex < this.mItems.size(); ++curIndex) {
                ViewPager.ItemInfo ii = (ViewPager.ItemInfo)this.mItems.get(curIndex);
                if (ii.position >= this.mCurItem) {
                    if (ii.position == this.mCurItem) {
                        curItem = ii;
                    }
                    break;
                }
            }
            //没找到就去创建 ViewPager.ItemInfo,并放入ArrayList<ViewPager.ItemInfo> mItems中
            if (curItem == null && N > 0) {
                curItem = this.addNewItem(this.mCurItem, curIndex);
            }

            int itemIndex;
            ViewPager.ItemInfo ii;
            int i;
            ·····一些计算操作,省略
        }
            
        this.mAdapter.finishUpdate(this);//完成条目的更新
        int childCount = this.getChildCount();

        for(itemIndex = 0; itemIndex < childCount; ++itemIndex) {
            View child = this.getChildAt(itemIndex);
            ViewPager.LayoutParams lp = (ViewPager.LayoutParams)child.getLayoutParams();
            lp.childIndex = itemIndex;
            if (!lp.isDecor && lp.widthFactor == 0.0F) {
                ViewPager.ItemInfo ii = this.infoForChild(child);
                if (ii != null) {
                    lp.widthFactor = ii.widthFactor;
                    lp.position = ii.position;
                }
            }
        }
        this.sortChildDrawingOrder();
    }
}

ViewPager通过计算当前页面的索引(mCurItem)和预加载页面的限制数量(mOffscreenPageLimit或pageLimit)来确定需要预加载的页面范围。通过Math.max(0, mCurItem - pageLimit)计算预加载的起始页面索引(startPos),通过Math.min(N-1, mCurItem + pageLimit)计算预加载的结束页面索引(endPos),N是页面总数。在这个范围内,ViewPager会提前加载并创建页面实例,以减少用户滑动到这些页面时的加载时间。

当ViewPager的当前页面改变时,会根据新的当前页面索引和预加载范围来加载或销毁页面实例。如果某个页面实例不再处于预加载范围内,它将被销毁以释放内存。

ViewPager.ItemInfo addNewItem(int position, int index) {
    ViewPager.ItemInfo ii = new ViewPager.ItemInfo();
    ii.position = position;
    
    ii.object = this.mAdapter.instantiateItem(this, position);
    ii.widthFactor = this.mAdapter.getPageWidth(position);
    if (index >= 0 && index < this.mItems.size()) {
        this.mItems.add(index, ii);
    } else {
        this.mItems.add(ii);
    }
    return ii;
}

调用instantiateItem来创建object,在FragmentPagerAdapter的instantiateItem这个方法中,创建的是Fragment,即缓存的就是Fragment,Fragment在创建时,会有UI操作,网络操作,在还未可见的时候初始化Fragment非常消耗性能,所以应该懒加载的方式来加载,不去缓存Fragment。

ViewPager屏蔽预加载方式:

  • 「设置setOffscreenPageLimit(int limit)方法的limit为0」:

理论上,这是最直接的方法,实际上setOffscreenPageLimit(int limit)方法中的limit值有一个最小值限制,即使你设置为0,也会自动调整为1。因为ViewPager的设计初衷就是为了预加载相邻的页面以提高滑动性能。

  • 「自定义ViewPager」:

「方法一」:复制ViewPager的源代码,并修改DEFAULT_OFFSCREEN_PAGES常量的值为0。

「方法二」:继承ViewPager类,重写setOffscreenPageLimit(int limit)方法,利用反射修改mOffscreenPageLimit属性的值。这种方法可能涉及到对Android系统内部实现的深入了解,并且可能随着Android版本的更新而失效。

  • 「调整PagerAdapter」:

可以通过调整PagerAdapter中的instantiateItem和destroyItem方法来控制页面的加载和销毁。例如,可以在这些方法中实现更精细的页面管理策略,以减少不必要的资源消耗。

  • 「懒加载技术」:

在Fragment或页面内容中使用懒加载技术。页面只有在真正需要显示时才会被加载。这可以通过在Fragment的setUserVisibleHint(boolean isVisibleToUser)方法中判断页面是否可见来实现。

  • 「注意事项」:

屏蔽预加载可能会影响ViewPager的滑动性能,当用户滑动到新的页面时,该页面可能需要花费更长的时间来加载。

在决定屏蔽预加载之前,权衡好性能和资源消耗之间的平衡。

责任编辑:武晓燕 来源: 沐雨花飞蝶
相关推荐

2011-03-17 10:37:07

JavaScript

2022-01-04 16:50:47

JavaScript图片网站

2024-10-22 08:00:00

2024-06-27 11:00:07

2021-08-04 09:51:02

代理设计模式

2023-04-18 23:44:54

性能优化开发preload

2019-09-24 09:25:05

Vue项目加载

2013-07-15 16:39:41

iOS多线程GCD实战串行队列实现预加载

2014-10-10 14:00:52

JavascriptHTML

2021-12-17 00:02:28

Webpack资源加载

2011-04-19 11:06:03

JavaScriptweb

2024-04-29 08:16:18

2011-06-09 16:14:14

2012-03-12 09:33:04

JavaScript

2020-07-29 12:16:12

预训练自训练神经网络

2022-11-18 14:46:38

数据中心数字孪生

2021-07-14 14:06:06

CSS前端浏览器

2011-04-11 14:56:09

Oracle性能

2024-02-02 09:21:57

API性能策略

2011-07-29 11:04:52

点赞
收藏

51CTO技术栈公众号