zoukankan      html  css  js  c++  java
  • 探究ListView 的缓存机制

    概述

    ListView 是继承AbListView,AbListView是所有列表类控件的基类。

    ListView的数据加载

    在ListView数据加载中最关键的一个函数就是makeAndAddView(),这个函数的作用就获得一个ChildView并把该ChildView添加到List中,具体见源码分析:

    private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
            boolean selected) {
        View child;//即ChildView
    
    	//如果数据没有发生改变
        if (!mDataChanged) {
            //优先从循环器中获取该位置的视图
            // Try to use an existing view for this position
            child = mRecycler.getActiveView(position);
            if (child != null) {
                // Found it -- we're using an existing child
                //如果找到了就直接添加到List中
                // This just needs to be positioned
                setupChild(child, position, y, flow, childrenLeft, selected, true);
    
                return child;
            }
        }
    
    	//如果数据发生了改变,则在该位置上新建一个视图,或者如果可能的话转换一个已经没有用的视图(可能是当整个ListView其他位置发生了变化,但是该位置的ChildView并未发生任何变化)
        // Make a new view for this position, or convert an unused view if possible
        child = obtainView(position, mIsScrap);
    
        // This needs to be positioned and measured
        setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);
    
    	//返回该childView
        return child;
    }
    

    ListView的缓存机制

    当ListView发生滑动操作时,若干已经加载的ChildView会被因滑动而被暂时隐藏掉,为了避免下次显示再重新加载,这时ListView的缓存机制就会被触发,即运行layoutChildren()函数(其实任何触碰事件都会触发,即onTouchEvent() -。-)。

    那么ListView的缓存机制是依靠什么来缓存的呢?答案就是AbListView中 的内部类RecycleBin。关于RecycleBin的具体作用,源码中的注释已经解释的非常清楚了,在此就不在赘述。

     /**
     * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
     * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
     * start of a layout. By construction, they are displaying current information. At the end of
     * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
     * could potentially be used by the adapter to avoid allocating views unnecessarily.
     *... ...
     */
    

    当需要缓存ActiveViews时会调用fillActiveViews()函数,该函数会把ListView中的所有ActiveViews 一次性都缓存起来。

    	/**
         * Fill ActiveViews with all of the children of the AbsListView.
         * ... ...
         */
        void fillActiveViews(int childCount, int firstActivePosition) {
            if (mActiveViews.length < childCount) {
                mActiveViews = new View[childCount];
            }
            mFirstActivePosition = firstActivePosition;
    
            //noinspection MismatchedReadAndWriteOfArray
            final View[] activeViews = mActiveViews;
    		... ...
        }
    

    而对于ScrapViews则是调用的addScrapView()函数。

    	/**
         * Puts a view into the list of scrap views.
         * <p>
         * If the list data hasn't changed or the adapter has stable IDs, views
         * with transient state will be preserved for later retrieval.
         *
         * @param scrap The view to add
         * @param position The view's position within its parent
         */
        void addScrapView(View scrap, int position) {
        ... ...
        // Don't scrap views that have transient state.
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (scrapHasTransientState) {
            //Transient状态
    		... ...
    		}else{
    		//scrap状态
    		... ...
    		}
    		... ...
        }
    

    该函数中又分为两个不同的level,一个是transient瞬时态,另一个就是一般的普通态,关于这两个状态的区分我个人的想法是为了更加快速的获取ScrapViews,因为处于瞬时状态的view最有可能是接下来将要在界面上显示的View,毕竟你向上或向下滑动列表时目的就是这个,这一点在obtainView()函数中得到了体现:

    View obtainView(int position, boolean[] isScrap) {
    ... ...
    //优先获取TransientStateView
    scrapView = mRecycler.getTransientStateView(position);
        if (scrapView == null) {
            scrapView = mRecycler.getScrapView(position);
        }
    ... ...
    }
    

    还有一个比较重要的函数就是scrapActiveViews()函数,它的作用是将目前所有的ActiveViews降级为ScrapViews,并将之前的所有ScrapViews清除。该函数在每次调用layoutChildern()函数时必定会被调用执行,目的就是为清空所有当前的ActiveViews,为新产生的ActiveViews做好准备。

        /**
         * Move all views remaining in mActiveViews to mScrapViews.
         */
        void scrapActiveViews() {
        ... ...
        //该函数确保mScrapViews的大小不会超过mActiveViews
        pruneScrapViews();
        }
    

    结语

    以上是阅读了ListView以及AbListView源码后的一些心得总结,毕竟阅读Android源码也才刚刚起步,还有很多地方理解的不是很透彻,上文若有理解不当之处欢迎各位指正。

    查看原文:http://www.xyczero.com/blog/article/14/.

  • 相关阅读:
    centos shell运行报语法错误: 未预期的文件结尾
    腾讯云防暴力破解防异地登陆
    centos常用命令
    centos7安装nginx
    JavaScript数组倒序函数reverse()
    Ecshop首页购物车数量调取问题
    (原)IPhone开发时把ToolBar中的元素居中的技巧
    iphone开发常用代码笔记
    Windows环境下使用Apache+mod
    [转]C++中sizeof(struct)怎么计算?
  • 原文地址:https://www.cnblogs.com/xyczero/p/4138407.html
Copyright © 2011-2022 走看看