关闭

财富坊cff888: Android性能:经典ListView适配器convertView缓存及复用机制

标签: android
3829人阅读 评论(4) 收藏 举报
分类:
Android性能:经典ListView适配器convertView缓存及复用机制


Android中的ListView常用Adapter中都会涉及到convertView的使用,使用convertView主要是为了缓存试图View,用以增加ListView的item view加载效率。有经验的Android开发者通常知道在Adapter的getView中,先判断convertView是否为空null,如果非空,则直接再次对convertView复用,否则才创建新的View。为何要复用convertView呢?参见我之前写的附录文章1。那么Android是如何维护convertView这一机制的运转呢?
(一)当第一次打开Android的ListView初始化时候,在Adapter中此时的convertView无疑均是空的,Android将创建新的convertView,创建多少呢?答案是一屏ListView有多少条item,就创建多少个convertView,注意,这仅仅是在第一次初始化ListView展现item时候才是这样的convertView创建机制,以一个ListView配置一个ArrayAdapter,写例子:
package zhangphil.demo;

import android.app.ListActivity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class MainActivity extends ListActivity {
    private int tagId = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ArrayAdapter mAdapter = new ArrayAdapter(this, 0) {
            private TextView text1;
            private TextView text2;

            @NonNull
            @Override
            public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
                if (convertView == null) {
                    convertView = LayoutInflater.from(getContext()).inflate(android.R.layout.simple_list_item_2, null);
                    //convertView.setMinimumHeight(400);

                    Log.d("位置" + position, "创建新convertView,设置tagId:" + tagId);
                    convertView.setTag(tagId++);
                } else {
                    Log.d("位置" + position, convertView.getTag() + " 复用convertView");
                }

                text1 = convertView.findViewById(android.R.id.text1);
                text1.setTextColor(getResources().getColor(android.R.color.holo_blue_bright));
                text1.setText(getItem(position));

                text2 = convertView.findViewById(android.R.id.text2);
                text2.setTextColor(getResources().getColor(android.R.color.holo_red_light));
                text2.setText("convertView tagId:" + String.valueOf(convertView.getTag()));

                return convertView;
            }

            @Nullable
            @Override
            public String getItem(int position) {
                return "位置position:" + position;
            }

            @Override
            public int getCount() {
                return 99999;
            }
        };

        setListAdapter(mAdapter);
    }
}

定义了一个tagId,用以标记convertView,在新创建的convertView里面打入进去,然后在后续convertView复用时候取出来,以追踪此时复用的到底是哪个convertView。

代码运行结果:




logcat日志输出结果:

11-03 14:40:08.745 22766-22766/zhangphil.demo D/位置0: 创建新convertView,设置tagId:0
11-03 14:40:08.760 22766-22766/zhangphil.demo D/位置1: 创建新convertView,设置tagId:1
11-03 14:40:08.765 22766-22766/zhangphil.demo D/位置2: 创建新convertView,设置tagId:2
11-03 14:40:08.773 22766-22766/zhangphil.demo D/位置3: 创建新convertView,设置tagId:3
11-03 14:40:08.782 22766-22766/zhangphil.demo D/位置4: 创建新convertView,设置tagId:4
11-03 14:40:08.787 22766-22766/zhangphil.demo D/位置5: 创建新convertView,设置tagId:5
11-03 14:40:08.795 22766-22766/zhangphil.demo D/位置6: 创建新convertView,设置tagId:6
11-03 14:40:08.800 22766-22766/zhangphil.demo D/位置7: 创建新convertView,设置tagId:7
11-03 14:40:16.573 22766-22766/zhangphil.demo D/位置8: 创建新convertView,设置tagId:8


可以看出,在ListView的初始化阶段,Android经过计算创建了九个全新的convertView,比当前设备屏幕可见区域的八条再多一条,我给这九个全新的convertView标记了tagId分别为0,1,2,3,4,5,6,7,8。这些打进去的tagId在后续可以追踪观察convertView的具体复用。
此处注意!根据每个屏幕的大小尺寸不一样,以及Adapter中getView返回的View高度不同,在不同的设备上,创建的新convertView个数不同。


(二)convertView发生作用的地方,就是当ListView在向上/向下滑动过程中,convertView缓存和复用机制才发挥出来。比如当手指在屏幕自下往上翻动ListView时候,此时ListView头部的item将滚出设备屏幕,而底部的新item将加载出来,此时convertView的复用机制将发挥作用。由于此前在ListView初始化阶段已经创建了九个全新的convertView,Android系统将之前ListView初始化阶段创建的九个全新convertView都缓存起来,现在,由于ListView的上下翻动,顶部和底部之前显示的item滚出设备屏幕不可见,Android系统要么完全回收这些convertView,要么复用这些convertView。ListView的item有一个共同点:在大多数情况下,这些item的View是相同的,所以,明智的做法是继续复用,这样无疑会提高系统加载性能,要知道每一次创建新的convertView,是有一定系统开销的。于是,在往下翻动ListView时候,代码运行结果如图所示:






logcat日志输出结果如图:
11-03 15:02:50.553 31901-31901/zhangphil.demo D/位置64: 1 复用convertView
11-03 15:02:50.637 31901-31901/zhangphil.demo D/位置65: 2 复用convertView
11-03 15:02:50.720 31901-31901/zhangphil.demo D/位置66: 5 复用convertView
11-03 15:02:50.820 31901-31901/zhangphil.demo D/位置67: 4 复用convertView
11-03 15:02:50.936 31901-31901/zhangphil.demo D/位置68: 7 复用convertView
11-03 15:02:51.069 31901-31901/zhangphil.demo D/位置69: 3 复用convertView
11-03 15:02:51.236 31901-31901/zhangphil.demo D/位置70: 8 复用convertView
11-03 15:02:51.452 31901-31901/zhangphil.demo D/位置71: 0 复用convertView
11-03 15:02:51.786 31901-31901/zhangphil.demo D/位置72: 6 复用convertView


(三)从第(二)小节的叙述和代码运行输出可见,Android的ListView在复用的缓存convertView过程中,并不保证是按顺序复用convertView的,这一点可以从输出的tagId可以看出来。tagId在每一轮复用convertView过程中的输出次序不是严格的0,1,2,3,4,5,6,7,8。而是呈现一定的随机性,但是,唯一可以保证的是,每一轮的复用,都必将把tagId编号0~8的这九个缓存convertView用完,只是不保证复用顺序。此处可以得到一定的启发,如果开发者打算按照某种特定顺序复用这九个缓存的convertView,那么可以在getView创建全新convertView时候,把这些新的convertView依次放入一个首尾相接的链表(一个闭环的圆圈链表),在本例中,该首尾相连的链表元素个数是9。开发者可以根据自己具体情况,从getView判断convertView为空时候作为定义该首尾相连链表长度的依据。


附录:
1,《基于“ViewHolder”技术提升Android ListView中Item View加载效率》链接:/bc5/zhangphil/article/details/44779723 
4
1
查看评论

浅析android适配器adapter中的那些坑

做项目中遇到的,折磨了我将近两天,今天把经验分享出来,让大家以后少走点弯路,好了,简单来说一下什么是android的适配器,如何定义,如何增加适配器的重用性,如何去降低程序的耦合性 适配器顾名思义是用来做适配的,但是他是如何做适配的,机制是什么,作用是什么,好,相信写java的都是知道什么是面向对...
  • qq_23195583
  • qq_23195583
  • 2015-07-10 12:55
  • 2245

ListView中convertView和ViewHolder的复用机制工作原理

我们都知道在getView方法中的操作是这样的:先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,找到每一个子View,如:一个TextView等。这里的findViewById操作是一个树查找过程,也是一...
  • dxcami
  • dxcami
  • 2016-04-09 21:47
  • 1856

Android AdapterView View的复用机制 分析

对于ListView、GridView相信大家都不陌生,重写个BaseView,实现对于的几个方法,然后就完成了我们的界面展示,并且在大部分情况下,我们加载特别多的Item也不会发生OOM,大家也都明白内部有缓存机制,都遇到过ItemView复用带来的一些问题,比如异步加载图片,最终造成界面显示的混...
  • lmj623565791
  • lmj623565791
  • 2014-04-23 00:21
  • 42000

listView的convertView复用机制

listView的convertView复用机制1、convertView的数量=屏幕item数量+item样式数量*1; 2、listView对convertView回收,根据getItemType(position)获取item样式数量,然后创建一个数组,元素为list集合,数组长度刚好为It...
  • snowle0pard
  • snowle0pard
  • 2015-09-27 15:15
  • 1056

ListView的优化之复用convertView和利用ViewHolder

ListView的优化之复用convertView和利用ViewHolder
  • hellobenji
  • hellobenji
  • 2016-07-01 14:12
  • 1708

listView的convertView复用引发的问题解决

import java.util.List; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content...
  • u012246348
  • u012246348
  • 2015-12-04 12:18
  • 439

解决BaseAdapter中多布局convertView复用加载错误

MyWidget mywidget; @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView==null) { mywidget=new MyWi...
  • An_Illusion
  • An_Illusion
  • 2016-05-15 14:07
  • 1360

本人原创!解决安卓ListView复用问题最简单的两种方法。

ListView在安卓很多地方需要用得到,其实listview是基于MVC架构设计的,即数据model,视图view和控制器controller。安卓已帮我们写好该框架,我们只需往里面塞数据就行。对于一些简单的listview,我们常用arrayadapter来作为适配器,但这往往不能达到我们想要的...
  • a1037949156
  • a1037949156
  • 2016-09-22 16:23
  • 2798

Android ListView缓存与复用

目录:BaseAdapter、ArrayAdapter、SimpleAdapterListView 用来显示列表 每一行内容称为一个条目 ListView的每一个条目都是一个View对象 不同布局文件中,资源id可以相同,找的时候注意设置在哪个布局中找 只要内存中有条目缓存,在新的条目出现时,就会使...
  • confusing_awakening
  • confusing_awakening
  • 2017-06-12 19:42
  • 306

ListView可复用适配器的封装及性能优化

0、前言  ListView是Android开发中最常用的控件之一,但在使用Adapter的时候若不进行合理的优化,容易造成资源浪费、代码冗余的情况,导致开发出来的APP性能低下。最开始接触适配器的封装是在慕课网鸿洋大神的教学视频里,后来在他的CSDN博客中也看到了这篇文章(点击此处浏览...
  • lyq7713
  • lyq7713
  • 2017-01-22 17:42
  • 404
    个人资料
    关于我
    邮箱: zhangphil@live.com
    博客专栏