优化ListView

1. 使用后台线程,让ListView流畅滚动

有经验的开发者经常建议:不要在主线程进行耗时的操作。但对于初学者来说,到底什么是主线程呢?这个问题我琢磨了很久。主线程其实就是UI线程,这个线程负责处理跟UI相关的操作。界面的绘制、界面的加载、控件的调整等操作都是由主线程来执行的。UI线程之所以被称作主线程,是因为在绝大多数情况下,我们写的代码都会在跑在UI线程里,比如界面的各个生命周期的回调方法(onCreate(),onResume(), onPause()等)。

由于主线程处理跟UI相关的操作,如果主线程处理耗时的操作(文件处理、网络请求等),那么就会导致主线程无法及时处理其他的UI操作请求,这样我们在界面上就会明显的感觉到卡顿。这个问题在处理ListView这种需要重复的绘制列表元素的情况下会变得更加严峻。因此需要小心处理ListAdapter里getView()方法里可能存在的耗时操作。

但使用后台线程就要接触多线程了,而多线程操作又是众所周知的坑多。如果不想自己手动去维护自己的线程池或实现自己的多线程机制,那么可以使用AsyncTask类来实现后台处理任务的需求。这是Android提供的一个非常便捷的方法。

  1. // 使用AsyncTask在后台加载一张加载很慢的图片
  2. // 这里的三个泛型参数的意义分别是:
  3. // 1) UI控件、2)进度回调的类型、3)任务执行完毕后的返回类型
  4. new AsyncTask<ViewHolder, Void, Bitmap>() {
  5.  
  6. // ViewHolder技术是实现复杂ListView的一个优化性能的技术,后面会介绍
  7. private ViewHolder v;
  8.  
  9. // doInBackground()方法在后台线程执行
  10. @Override
  11. protected Bitmap doInBackground(ViewHolder... params) {
  12. v = params[0]; // 这里保存一份界面的引用
  13.  
  14. // 这里假定有一个图片加载器,getImage()方法是它的一个耗时方法
  15. return mFakeImageLoader.getImage();
  16. }
  17.  
  18. // onPostExecute()方法在主线程执行,确保所有耗时的操作在doInBackground()方法做完了
  19. @Override
  20. protected void onPostExecute(Bitmap result) {
  21. super.onPostExecute(result);
  22. if (v.position == position) {
  23. v.progress.setVisibility(View.GONE);
  24. v.icon.setVisibility(View.VISIBLE);
  25. v.icon.setImageBitmap(result);
  26. }
  27. }
  28.  
  29. }.execute(holder);

从Android 3.0(API 11)开始,AsyncTask增加了一个方法:AsyncTask.executeOnExecutor()。这个方法会利用处理器的多核特性,进一步提升性能。这个方法的具体效果取决于具体设备的处理器核心数。

2. 使用ViewHolder技术来优化ListView

前面说到,在ListView这种需要重复的绘制列表元素的情况下,性能问题将变得更加严峻。由于内存的原因,ListView只有一个固定数量的View列表来显示列表的每一项,通过回收不可见的项,重新调整控件来显示新出现在界面上的项。因此在ListView的滚动过程中,将会频繁的调用ListAdapter.getView()方法。通过上面的优化建议,我们已经把耗时操作放到后台线程去执行了,只把必须的UI操作留在主线程。那还能不能再优化呢?答案是肯定的。

如果有经常在网上查找教程,那么应该对ListView的教程里的ViewHolder技术不陌生。这个技术的核心是减少主线程里的执行步骤,以达到优化性能的目的。通过使用ViewHolder,缓存每个View的引用,减少不必要的查找控件操作,以达到优化ListView性能的效果。

首先需要创建一个ViewHolder类来持有View的引用:

  1. static class ViewHolder {
  2. TextView text;
  3. TextView timestamp;
  4. ImageView icon;
  5. ProgressBar progress;
  6. }

然后实例化ViewHolder并为其赋值,再将ViewHolder存储在view的tag里:

  1. ViewHolder holder = new ViewHolder();
  2. holder.text = (TextView) convertView.findViewById(R.id.list_item_text);
  3. holder.timestamp = (TextView) convertView.findViewById(R.id.list_item_timestamp);
  4. holder.icon = (ImageView) convertView.findViewById(R.id.list_item_icon);
  5. holder.progress = (ProgressBar) convertView.findViewById(R.id.list_item_progress);
  6. convertView.setTag(holder);

之后就可以从convertView的tag里取出ViewHolder来直接访问对应的控件进行操作了。

  1. ViewHolder holder = (ViewHolder) convertView.getTag();
  2. holder.text.setText(text);
  3. // ...这里省略对其他控件的操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值