Listview异步加载缓存图片,解决快速滑动问题-Android开发源码下载-eoe Android开发者社区_Android开发论坛

Android 4.0

  • 用户名
  • Email
登录 后使用快捷导航
没有帐号?免费加入
查看: 7044|回复: 205

Listview异步加载缓存图片,解决快速滑动问题

  [复制链接]
天宇 当前离线
积分
5728

48

主题

1616

帖子

1539

e币

实习版主

积分
5728

楼主
发表于 2013-4-25 11:18:10 | 只看该作者 |倒序浏览 |阅读模式
+1
0
本帖最后由 天宇 于 2013-4-28 13:30 编辑

   研究了一天,终于达到了自己想要学习的目的:缓存图片+异步加载,

   搜了大量资料见的大多避免oom的方法有压缩和缓存,这里也采用这2种方法吧。

   压缩就不用我说了,缓存图片我用的LruCache这个类,本身已经实现了同步,这里就不再多说什么了,不知道的同学可以去研究下,这里主要想讲的的异步加载的时机。在这里写下也只是分享下我的体验,欢迎拍砖~~~

  大家做过这个的都碰到过,快速滑动时由于大量异步加载和message消息的等待排队,当快速滑动停止时要等好一会才能轮到当前可视item图片的加载显示,如何避免这个问题?

  那就从问题来源入手:快速滑过的那些item图片可以先暂不加载,等到用户正常划过时再去加载不迟~~~
  
  我的代码思路:在getView方法里面判断Listview的滑动状态,如果正在滑动,则不加载,但要保存此时的图片信息,等到适当机会再去加载,否则异步加载图片~~~

  实现Listview状态的标识,代码:
  
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
listView.setOnScrollListener(new OnScrollListener() {
                        @Override
                        public void onScrollStateChanged(AbsListView view, int scrollState) {
                                //标识正在滑动中
                                if(scrollState==OnScrollListener.SCROLL_STATE_FLING){
                                        isBusy=true;
                                }else {
                                        isBusy=false;
                                        asyncLoading();
                                }
                        }
                         
                        @Override
                        public void onScroll(AbsListView view, int firstVisibleItem,
                                        int visibleItemCount, int totalItemCount) {
                                firstItem=firstVisibleItem;
                                bottmItem=firstItem+visibleItemCount;
                        }
                });
        }


isBusy用来标识Listview是否处在滑动状态,asyncLoading()方法下面会说到

现在知道了当前listview的滚动状态,那么现在就开始在getview里面加载资源吧:
  
代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
                        Bitmap bitmap=cache.get(""+position);//取出缓存中的bitmap
                        final ViewHolder holder;
                        if(convertView==null){
                                holder=new ViewHolder();
                                convertView=getLayoutInflater().inflate(R.layout.item, null);
                                holder.txt=(TextView) convertView.findViewById(R.id.txt);
                                holder.img=(ImageView) convertView.findViewById(R.id.img);
                                convertView.setTag(holder);
                        }else {
                                holder=(ViewHolder) convertView.getTag();
                        }
                          if(bitmap==null){//如果没有缓存
                                                if(!isBusy){//判断当前listview不在滑动状态
                                                        Log.d("==========", "正常加载当前位置图片:"+position);
                                                        executor.execute(new Runnable() {//异步加载图片
                                                                @Override
                                                                public void run() {
                                                                        Bitmap bitmap=DownLoadImage();//模拟下载图片
                                                                        if(bitmap!=null){
                                                                                cache.put(""+position, bitmap);
                                                                                Message message=handler.obtainMessage();
                                                                                message.obj=holder.img;
                                                                                message.arg1=position;
                                                                                message.what=LOADIMAGE;
                                                                                handler.sendMessage(message);//图片下载完成后通知更新
                                                                        }
                                                                }
                                                        });
                                                }
                                                else {//如果当前listview在快速滑动状态
                                                        Message message=handler.obtainMessage();
                                                        message.obj=holder.img;
                                                        message.arg1=position;
                                                        message.what=LOADIMAGE;
                                                        messages.add(message);///记录当前getview的图片资源信息,保存在message中,放入到集合等待加载
                                                }
                                                holder.img.setImageBitmap(defaultBitmap);//设置默认图片,等图片资源下载完毕后在更新
                                        }else {
                                                holder.img.setImageBitmap(bitmap);
                                        }
                        holder.txt.setText(position+"");
                        return convertView;
                


代码的注释很清楚了,我在这里再说下,如果当前listview不在滑动状态时,正常加载图片,否则就记录当前的图片信息,存放到一个集合中,等待适当的机会去加载,什么时候适当呢?那就是listview不在滑动的时候,即isbusy=false时
那回过头来看上面出现过的asyncLoading()这个方法吧

代码片段,双击复制
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private  void asyncLoading(){
                int size=messages.size();
                for(int i=size-1;i>=0;i--){//从集合的末尾开始遍历
                        final Message msg=messages.get(i);
                        if(msg.arg1>=firstItem-1&&msg.arg1<bottmItem+1){//取出当前的position(也就是msg.arg1),判断该position是否在当前可视位置,(如果不要这个判断的话,则加载所有先前滑动过去的item的图片)
                                Log.e("==========", "滑动结束,手动加载当前位置图片:"+msg.arg1);
                                cache.put(msg.arg1+"", defaultBitmap);//这边一定要将position事先存放进去,占个位置,表明该位置已经有图片在下载了,不然可能会出现重复下载的情况
                                FILLINGexecutor.execute(new Runnable() {
                                        @Override
                                        public void run() {
                                                Bitmap bitmap=DownLoadImage();
                                                if(bitmap!=null){
                                                        cache.put(msg.arg1+"", bitmap);//下载完成后,再将defaultBitmap图片覆盖
                                                        handler.sendMessageAtFrontOfQueue(msg);//放到消息队前,因为要最先加载当前可视的item图片资源
                                                }
                                        }
                                });
                        }else {
                                break;//跳出循环(只要当前不满足,之后的也不会满足)
                        }
                }
                messages.clear();//清空消息
        }


注释很清楚了,也就不再说了。

总之,demo实现了这样一个效果,每个item的图片都属于自己,没有重用(因为同一张图片每次都是重新加载到内存的),当正常滑动listview时,则正常加载图片,当快速滑动时,只加载listview停下后可视item的图片,这就避免了等待啦,因为之前被滑过item的图片没有被异步加载哦~~欢迎提出意见,大家一起学习~~~

当然,真正的应用是不只在getview方法里面去加载的,因为滑动过后才会去加载,体验效果会很差,这里只是学习用


存在错位bug:

你会发现有时同一张图片会连续变动1、2次,甚至更多次,那是因为convertView缓存的缘故,
比如消息队列中有消息要通知刷新position=10,16,22这3个图片的位置,而这三个item恰好使用同一个convertView,所以他们也是使用的同一个ImageVIew
此时position=22的item是可视
那么这个ImageVIew.setImageBitmap()的方法会被连续调用3次
导致你们会看到图片连续变动

这个bug在本demo中很好解决,2个方法:
1:就是判断当前可视的item位置再去设
2:在手动发送message之前清空消息队列(这种方法适合于一定要是非常快速滑动的那种,因为listview稍微滑下就会处于busy状态,此时正常加载的position还是可视的)

提出问题:如果一开始就去加载全部图片后缓存,而不是在getview里面开线程去加载图片,那么如何通知到对应的图片UI更新以及错位呢?这过程会发生什么问题?你可以亲自动手实现下,提醒下加载图片是耗时的哦~~~
tip:我自己碰到了message消息过期的问题,正在解决中。。



奉上代码:主要是图片大,代码就一个类

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?免费加入

x

本帖被以下淘专辑推荐:

天宇 当前离线
积分
5728

48

主题

1616

帖子

1539

e币

实习版主

积分
5728

推荐
 楼主| 发表于 2013-9-17 13:42:17 | 只看该作者
knyouoo 发表于 2013-9-16 11:12
请问你说的那个message消息过期的问题,解决了吗?我感觉现在有时候还是会出现图片错位或不加载的情况,就 ...

预加载才会碰到,其实也很好解决,同样还是判断item是否可视然后设置,当然消息队列里面可能要清空之前的消息  看具体情况
knyouoo 当前离线
积分
2292

升级   82.67%

当前用户组为 No.5 砖家开发者
当前积分为 2292, 升到下一级还需要 208 点。

19

主题

614

帖子

1011

e币

No.5 砖家开发者

No.5 砖家开发者, 积分 2292, 距离下一级还需 208 积分

No.5 砖家开发者, 积分 2292, 距离下一级还需 208 积分
积分
2292

推荐
发表于 2013-9-5 15:01:56 | 只看该作者
天宇 发表于 2013-9-5 14:42
自己可以拷贝下源码~~
当然也可以自己写,网上有类似的源码可以参考下, ...

我把源码弄下来了,但是源码中的这一句会报错
Map.Entry<K, V> toEvict = map.eldest();

http://www.eoeandroid.com/thread-286816-1-1.html

点评

这个我倒没研究过,报错的话你看看log修复下吧 要是不行就按照源码思路自己写个简易的  发表于 2013-9-9 11:14
姬蕊 当前离线
积分
1578

升级   23.17%

当前用户组为 No.5 砖家开发者
当前积分为 1578, 升到下一级还需要 922 点。

0

主题

464

帖子

173

e币

No.5 砖家开发者

No.5 砖家开发者, 积分 1578, 距离下一级还需 922 积分

No.5 砖家开发者, 积分 1578, 距离下一级还需 922 积分
积分
1578
推荐
发表于 2013-9-13 08:22:51 | 只看该作者
不错奥                                         
heliewan 当前离线
积分
2624

升级   5.17%

当前用户组为 No.6 江湖开发者
当前积分为 2624, 升到下一级还需要 2276 点。

31

主题

724

帖子

1950

e币

No.6 江湖开发者

No.6 江湖开发者, 积分 2624, 距离下一级还需 2276 积分

No.6 江湖开发者, 积分 2624, 距离下一级还需 2276 积分
积分
2624

沙发
发表于 2013-4-25 12:08:22 | 只看该作者
下载看看。。
x838328190 当前离线
积分
985

升级   47.5%

当前用户组为 No.4 中级开发者
当前积分为 985, 升到下一级还需要 315 点。

9

主题

249

帖子

534

e币

No.4 中级开发者

No.4 中级开发者, 积分 985, 距离下一级还需 315 积分

No.4 中级开发者, 积分 985, 距离下一级还需 315 积分
积分
985

板凳
发表于 2013-4-25 15:00:30 | 只看该作者
看看。。。
chinapengwei 当前离线
积分
2337

升级   86.42%

当前用户组为 No.5 砖家开发者
当前积分为 2337, 升到下一级还需要 163 点。

2

主题

395

帖子

824

e币

No.5 砖家开发者

No.5 砖家开发者, 积分 2337, 距离下一级还需 163 积分

No.5 砖家开发者, 积分 2337, 距离下一级还需 163 积分
积分
2337
地板
发表于 2013-4-25 18:16:48 | 只看该作者
不错,有空学习一下。

点评

欢迎拍砖  详情 回复 发表于 2013-4-25 18:20
天宇 当前离线
积分
5728

48

主题

1616

帖子

1539

e币

实习版主

积分
5728

5
 楼主| 发表于 2013-4-25 18:20:42 | 只看该作者
chinapengwei 发表于 2013-4-25 18:16
不错,有空学习一下。

欢迎拍砖
qqshuai 当前离线
积分
206

升级   53%

当前用户组为 No.2 初级开发者
当前积分为 206, 升到下一级还需要 94 点。

1

主题

54

帖子

237

e币

No.2 初级开发者

No.2 初级开发者, 积分 206, 距离下一级还需 94 积分

No.2 初级开发者, 积分 206, 距离下一级还需 94 积分
积分
206
6
发表于 2013-4-26 09:46:50 | 只看该作者
mark一下
家最乐 当前离线
积分
519

升级   54.75%

当前用户组为 No.3 秀才开发者
当前积分为 519, 升到下一级还需要 181 点。

0

主题

157

帖子

0

e币

No.3 秀才开发者

No.3 秀才开发者, 积分 519, 距离下一级还需 181 积分

No.3 秀才开发者, 积分 519, 距离下一级还需 181 积分
积分
519
7
发表于 2013-4-26 10:13:37 | 只看该作者
xiaodatao 当前离线
积分
835

升级   22.5%

当前用户组为 No.4 中级开发者
当前积分为 835, 升到下一级还需要 465 点。

3

主题

235

帖子

122

e币

No.4 中级开发者

No.4 中级开发者, 积分 835, 距离下一级还需 465 积分

No.4 中级开发者, 积分 835, 距离下一级还需 465 积分
积分
835
8
发表于 2013-4-26 14:16:03 | 只看该作者
不错,有空学习一下。
过期的白砂糖 当前离线
积分
2553

升级   2.21%

当前用户组为 No.6 江湖开发者
当前积分为 2553, 升到下一级还需要 2347 点。

47

主题

621

帖子

7036

e币

No.6 江湖开发者

No.6 江湖开发者, 积分 2553, 距离下一级还需 2347 积分

No.6 江湖开发者, 积分 2553, 距离下一级还需 2347 积分
积分
2553

9
发表于 2013-4-27 11:31:48 | 只看该作者
谢谢分享~看看
维尼熊 当前离线
积分
684

升级   96%

当前用户组为 No.3 秀才开发者
当前积分为 684, 升到下一级还需要 16 点。

3

主题

186

帖子

73

e币

No.3 秀才开发者

No.3 秀才开发者, 积分 684, 距离下一级还需 16 积分

No.3 秀才开发者, 积分 684, 距离下一级还需 16 积分
积分
684
10
发表于 2013-4-27 16:58:24 | 只看该作者
下来看看
冰蓝骑士 当前在线
积分
3385

升级   36.88%

当前用户组为 No.6 江湖开发者
当前积分为 3385, 升到下一级还需要 1515 点。

35

主题

603

帖子

1466

e币

No.6 江湖开发者

No.6 江湖开发者, 积分 3385, 距离下一级还需 1515 积分

No.6 江湖开发者, 积分 3385, 距离下一级还需 1515 积分
积分
3385
11
发表于 2013-4-27 17:27:45 | 只看该作者
OOM大敌打,学习学习

点评

呵呵,欢迎拍砖  详情 回复 发表于 2013-4-27 17:34
天宇 当前离线
积分
5728

48

主题

1616

帖子

1539

e币

实习版主

积分
5728

12
 楼主| 发表于 2013-4-27 17:34:42 | 只看该作者
冰蓝骑士 发表于 2013-4-27 17:27
OOM大敌打,学习学习

呵呵,欢迎拍砖
1016787958 当前离线
积分
730

升级   5%

当前用户组为 No.4 中级开发者
当前积分为 730, 升到下一级还需要 570 点。

0

主题

178

帖子

13

e币

No.4 中级开发者

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分
积分
730
13
发表于 2013-4-27 18:21:45 | 只看该作者
谢谢分享哦。。。。哈哈哈
1016787958 当前离线
积分
730

升级   5%

当前用户组为 No.4 中级开发者
当前积分为 730, 升到下一级还需要 570 点。

0

主题

178

帖子

13

e币

No.4 中级开发者

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分
积分
730
14
发表于 2013-4-27 18:22:55 | 只看该作者
谢谢分享哦。。。。。。。。。。。。。。
1016787958 当前离线
积分
730

升级   5%

当前用户组为 No.4 中级开发者
当前积分为 730, 升到下一级还需要 570 点。

0

主题

178

帖子

13

e币

No.4 中级开发者

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分

No.4 中级开发者, 积分 730, 距离下一级还需 570 积分
积分
730
15
发表于 2013-4-27 18:23:19 | 只看该作者
下载下来看看哦。。。。。。。。。。。。哈哈哈

点评

感谢支持~~~  详情 回复 发表于 2013-4-27 18:53
您需要登录后才可以回帖 登录 | 免费加入

本版积分规则

QQ认证

QQ已认证,此人靠谱

2013新年勋章

2013新年勋章

社区认证会员

社区认证会员

五级勋章

五级勋章

四级勋章

四级勋章

关于我们|聚贤厅|android培训|小黑屋|Archiver|手机版|eoe Android开发者社区 ( 京ICP备11018032 京公网安11010802011031   

GMT+8, 2013-12-6 10:34 , Processed in 0.174709 second(s), 13 queries , Memcache On.