对于ListView、GridView相信大家都不陌生,重写个BaseView,实现对于的几个方法,然后就完毕了我们的界面展示。而且在大部分情况下,我们载入特别多的Item也不会发生OOM,大家也都明确内部有缓存机制,都遇到过ItemView复用带来的一些问题,比方异步载入图片。终于造成界面显示的混乱。我们通常会使用setTag。然后回调显示时,避免造成混乱。

设想1:拿ListView为例,假设ListView的ItemView复用机制,全部的ItemView复用同一个。假设在多线程下载图片的情况下,可能终于仅仅有最后一个View显示图片吧。由于你前面的设置setTag(url),后面立即就会将你的Tag的值覆盖掉。终于findViewByTag找到的都是最后一个。

由此可见ListView缓存的不是一个。至少是一屏幕可显示的数量。

也就是说ListView维护着一个ItemView的池子。

跟大家解释下。为啥缓存了一个屏幕的可显示最大的ItemView数量的池子。我们可能上千个ItemView。仅依靠Tag就能实现不混乱呢。

情景:屏幕每次显示7个Item,ListView一共1000个Item,每一个Item上显示一张从网络下载的图片。

getView的代码大概是这种:

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
final String url = getItem(position);
View view;
if (convertView == null)
{
view = LayoutInflater.from(getContext()).inflate(R.layout.photo_layout, null);
} else
{
view = convertView;
}
final ImageView photo = (ImageView) view.findViewById(R.id.photo);
// 给ImageView设置一个Tag,保证异步载入图片时不会乱序
photo.setTag(url);
new LoadImgTask(photo).execute(url);
return view;
}

下载完毕图片。进行photo.getTag().equals(url)来防止图片显示的混乱。

假设我们打开界面,开启了7个线程去下载。此时缓存了这7个ItemView,如今滑动屏幕显示另外下一屏。此时7个ItemView都会复用,会把第一屏设置的Tag所有覆盖掉。没错就是覆盖掉了,又开启7个线程去下载图片。当第一屏的ItemView的图片下载完毕后。假设直接findViewByTag然后设置图片会显示在第二屏上。就混乱了。所以一般在显示前都会推断photo.getTag().equals(url);确定了再显示。也就是说第一屏的ItemView图片下载完了,可是Tag被覆盖了,所以即使下载完毕了,也不会有不论什么显示。

这就解释了为什么我们防止混乱的代码须要那样去写。

好了,以下从源代码角度看一眼ListView内部究竟是怎样进行缓存的:

跟着ListView,进入父类AbsList,会发现这样一个变量:

 /**
* The data set used to store unused views that should be reused during the next layout
* to avoid creating new ones
*/
final RecycleBin mRecycler = new RecycleBin();

凝视的意思上用一个数据集来存储应当在下一个布局重用的View。避免又一次创建新的布局。这个对象应该就是对我们缓存管理的核心类了。

继续看这个类。这是一个内部类:

	/**
* 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.
*
* @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
* @see android.widget.AbsListView.RecyclerListener
*/
class RecycleBin { private View[] mActiveViews = new View[0]; private ArrayList<View>[] mScrapViews; .... }

大概意思:这个类是用来帮助在滑动布局时重用View的,RecycleBin包括了两个级别的存储。ActiveViews和ScrapViews,ActiveViews存储的是第一次显示在屏幕上的View;全部的ActiveViews终于都会被移到ScrapViews。ScrapViews存储的是有可能被adapter复用的View。

如今非常明白了AbsListView缓存依赖于两个数组。一个数组存储屏幕上当前现实的ItemView,一个显示从屏幕下移除的且可能会被复用的ItemView。以下看ListView里面的代码:

@Override
protected void layoutChildren()
{
if (dataChanged)
{
for (int i = 0; i < childCount; i++)
{
recycleBin.addScrapView(getChildAt(i));
}
} else
{
recycleBin.fillActiveViews(childCount, firstPosition);
}
....
}
 /**
* Fill ActiveViews with all of the children of the AbsListView.
*
* @param childCount The minimum number of views mActiveViews should hold
* @param firstActivePosition The position of the first view that will be stored in
* mActiveViews
*/
void fillActiveViews(int childCount, int firstActivePosition)
{
if (mActiveViews.length < childCount)
{
mActiveViews = new View[childCount];
}
mFirstActivePosition = firstActivePosition; final View[] activeViews = mActiveViews;
for (int i = 0; i < childCount; i++)
{
View child = getChildAt(i);
activeViews[i] = child;
}
}

能够看出,假设数据发生变化则把当前的ItemView放入ScrapViews中,否则把当前显示的ItemView放入ActiveViews中。那么咱们关键的getView方法究竟是在哪调用呢。以下看RecycleBin中的方法:

/**
* Get a view and have it show the data associated with the specified
* position. This is called when we have already discovered that the view is
* not available for reuse in the recycle bin. The only choices left are
* converting an old view or making a new one.
*
* @param position The position to display
* @param isScrap Array of at least 1 boolean, the first entry will become true if
* the returned view was taken from the scrap heap, false if otherwise.
*
* @return A view displaying the data associated with the specified position
*/
View obtainView(int position, boolean[] isScrap)
{
isScrap[0] = false;
View scrapView;
scrapView = mRecycler.getScrapView(position);
View child;
if (scrapView != null)
{ child = mAdapter.getView(position, scrapView, this);
if (child != scrapView)
{
mRecycler.addScrapView(scrapView); } else
{
isScrap[0] = true;
child.dispatchFinishTemporaryDetach();
}
} else
{
child = mAdapter.getView(position, null, this);
} return child;
}

能够看到,这种方法就是返回当前一个布局用户当前Item的显示。首先依据position去ScrapView中找,找到后调用我们的getView,此时getView里面的convertView!=null了。然后getView假设返回的View发生变化。缓存下来。否则convertView==null了。

好了。主要是为了让大家了解,AbsListView为什么我们能够通过一个Tag的设置保证其正确的显示,以及缓存机制在AbsListView究竟是怎么实现的。鉴于源码实在太长。仅仅能大概的依据代码了解一下原理。

最后,各位看官。没事留个言,顶一个呗~

Android AdapterView View的复用机制 分析的更多相关文章

  1. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  2. Android 中View的绘制机制源代码分析 一

    尊重原创: http://blog.csdn.net/yuanzeyao/article/details/46765113 差点儿相同半年没有写博客了,一是由于工作比較忙,二是认为没有什么内容值得写, ...

  3. Android 中View的绘制机制源代码分析 二

    尊重原创:http://blog.csdn.net/yuanzeyao/article/details/46842891 本篇文章接着上篇文章的内容来继续讨论View的绘制机制,上篇文章中我们主要解说 ...

  4. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  5. Android AdapterView 源码分析以及其相关回收机制的分析

    忽然,发现,网上的公开资料都是教你怎么继承一个baseadapter,然后重写那几个方法,再调用相关view的 setAdpater()方法, 接着,你的item 就显示在手机屏幕上了.很少有人关注a ...

  6. Android应用程序组件Content Provider的共享数据更新通知机制分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6985171 在Android系统中,应用程序组 ...

  7. Android学习笔记之ListView复用机制

    PS:满打满算,差不多三个月没写博客了...前一阵忙的不可开交...总算是可以抽出时间研究研究其他事情了... 学习内容: 1.ListView的复用机制 2.ViewHolder的概念 1.List ...

  8. Android应用层View绘制流程与源码分析

    1  背景 还记得前面<Android应用setContentView与LayoutInflater加载解析机制源码分析>这篇文章吗?我们有分析到Activity中界面加载显示的基本流程原 ...

  9. Android视图状态及重绘流程分析,带你一步步深入了解View(三)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17045157 在前面一篇文章中,我带着大家一起从源码的层面上分析了视图的绘制流程, ...

随机推荐

  1. DES加解密算法Qt实现

      算法解密qt加密table64bit [声明] (1) 本文源码 大部分源码来自:DES算法代码.在此基础上,利用Qt编程进行了改写,实现了DES加解密算法,并添加了文件加解密功能.在此对署名为b ...

  2. IO-File 文件 目录 基本操作 递归 遍历

    创建和删除 //创建文件 File file1 = new File("不存在的文件.txt");//注意,这一步并没有创建文件,只是把磁盘中的文件封装成了一个对象 System. ...

  3. 添加序号列(SQL Server)

    SELECT ROW_NUMBER() OVER (ORDER BY 实际缴费金额 ) AS A, --序号 RANK() OVER (ORDER BY 实际缴费金额 ) AS B, --相同跳过从新 ...

  4. SQL Server中的临时表和表变量 Declare @Tablename Table

    在SQL Server的性能调优中,有一个不可比面的问题:那就是如何在一段需要长时间的代码或被频繁调用的代码中处理临时数据集?表变量和临时表是两种选择.记得在给一家国内首屈一指的海运公司作SQL Se ...

  5. C#中的TCP通讯与UDP通讯

    最近做了一个项目,主要是给Unity3D和实时数据库做通讯接口.虽然方案一直在变:从开始的UDP通讯变为TCP通讯,然后再变化为UDP通讯;然后通讯的对象又发生改变,由与数据库的驱动进行通讯(主动推送 ...

  6. 动态绑定、阻止继承,final类和方法

    1.编译器查看对象的声明类型和方法名.当调用 x.f(param); 且隐式参数x生命为C类对象.这时候可能有多个名字都叫f,但是参数类型不一样的方法.编译器会一一列举C类中名为f的方法和其超类中访问 ...

  7. Altium Designer极坐标布局方法

    1.键盘快捷组合 O+G,打开栅格管理器,点击左下角的“菜单”,在对话框中的选择“添加极坐标栅格”. 2.双击新添加的优先等级为1的栅格,在弹出的polar  grid editor 对话框中,对里边 ...

  8. 轻量级jQuery工具提示插件tooltipsy使用方法

    今天想给单位的系统弄一个提示插件,懒得自己做,于是就上网百度了几个jQuery插件,挺好用的.下面以tooltipsy插件为例,说明如何使用这些插件. 一.下载 首先到tooltipsy的官网http ...

  9. CSS应用五

    1. 页面变灰 html {   filter: grayscale(100%);//IE浏览器   -webkit-filter: grayscale(100%);//谷歌浏览器   -moz-fi ...

  10. IOS中获取各种文件的目录路径的方法-备

    iphone沙箱模型的有四个文件夹,分别是什么,永久数据存储一般放在什么位置,得到模拟器的路径的简单方式是什么. documents,tmp,app,Library. (NSHomeDirectory ...