ListView异步加载网络图片之完美解决方案
问题描述:上一篇文章中解决了一个图片显示混乱的bug,但是还遗留下来一个更严重的bug,那就是当我们猛地拖动列表的时候,会感觉非常的卡顿,并且继续不顾一切的拖动程序就会崩溃,看一下抛出的异常是RejectedExecutionException。
错误的原因:由于异步加载图片用的是AsyncTask(异步任务),AsyncTask的内容实现是采用的一个线程池,池子的的最大容量是128,然而连续的滚动ListView触发了getView的调用,在getView中又去创建并执行了异步任务,所以就会导致线程池满了,看一下AsyncTask的源码
//池子的最大容量
private static final int MAXIMUM_POOL_SIZE = 128;
//池子
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,
sWorkQueue, sThreadFactory);
从AysncTask的execute方法中调用线程数的方法sExecutor.execute(mFuture),下面就是线程池的execute方法,在这个方法中由于线程池满了,所以命名会被拒绝执行也就会调用reject(command) 最终将会抛出RejectedExecutionException异常。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
追踪到抛出异常的代码是在AbortPolicy类,AbortPolicy实现了RejectedExecutionHandler接口因此实现了rejectedExecution方法
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
// BEGIN android-changed
// provide diagnostic messaging for a common exception
// a message is helpful even if it isn't created atomically
int queueSize = e.getQueue().size();
int remainingCapacity = e.getQueue().remainingCapacity();
String message = "pool=" + e.getPoolSize() + "/" + e.maximumPoolSize
+ ", queue=" + queueSize;
if (remainingCapacity != Integer.MAX_VALUE) {
message += "/" + (queueSize + remainingCapacity);
}
throw new RejectedExecutionException(message);
// END android-changed
}
解决的方法:产生问题的原因是执行了太多的异步任务,所以解决问题的方法就是,减少异步任务的创建,那么如何减少呢?我们知道ListView在的滚动有3个状态
- SCROLL_STATE_FLING(惯性滚动)
- SCROLL_STATE_IDLE(空闲)
- SCROLL_STATE_TOUCH_SCROLL(拖动)
当ListView在处于SCROLL_STATE_FLING状态的时候由于是快速的从屏幕上面飞过,其实没有必要创建异步任务下载图片,只有当ListView处于低速的拖动状态或者是空闲状态的时候才去创建异步任务,这样就会大大的降低异步任务的数量。
那么可以在adapter中通过一个变量mBusy来控制adapter的状态当mBusy==false的时候才去创建异步任务,并且我们给ListView设置一个setOnScrollListener,在监听器的回调函数中改变adapte的mBusy的值,达到控制是否创建异步任务的作用
OnScrollListener mScrollListener = new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setFlagBusy(true);
break;
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.setFlagBusy(false);
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setFlagBusy(false);
break;
default:
break;
}
adapter.notifyDataSetChanged();
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
}
};
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Log.d(TAG, "position=" + position + ",convertView=" + convertView);
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.list_item, null);// 这个过程相当耗时间
viewHolder = new ViewHolder();
viewHolder.mTextView = (TextView) convertView
.findViewById(R.id.tv_tips);
viewHolder.mImageView = (ImageView) convertView
.findViewById(R.id.iv_image);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
String url = "";
url = URLS[position % URLS.length];
if (!mBusy) {
mImageLoader.loadImage(url, this, viewHolder);
viewHolder.mTextView.setText("--" + position
+ "--IDLE ||TOUCH_SCROLL");
} else {
Bitmap bitmap = mImageLoader.getBitmapFromCache(url);
if (bitmap != null) {
viewHolder.mImageView.setImageBitmap(bitmap);
} else {
viewHolder.mImageView.setImageResource(R.drawable.ic_launcher);
}
viewHolder.mTextView.setText("--" + position + "--FLING");
}
return convertView;
}
总结:本文对前一个demo遗留的bug做了讲解并解决,在此基础上还对程序做了进一步的优化,这次的demo通过双缓冲技术对数据进行缓存,代码已经上传,同学们可以自己下载下来研究一下双缓冲技术。我在下一篇博客中会对双缓冲的代码进行详细的分析,谢谢您看到了博客的结尾。
- 大小: 10.4 KB
分享到:
相关推荐
10. **总结**:在Android的ListView中实现异步加载网络图片,需要结合异步处理框架、选择合适的图片库、优化缓存策略、合理管理内存,并对ListView进行优化。通过这些手段,可以显著提升应用的性能和用户体验。
因此,"ListView异步加载网络图片"是一个重要的优化技巧。 异步加载的基本思路是将图片下载和显示的操作放在后台线程进行,避免占用主线程资源,确保UI的流畅。在Android中,可以使用多种方式实现这一目标,例如...
为了解决这个问题,我们需要实现ListView的异步加载图片功能。本篇文章将详细介绍如何通过软引用缓存图片,实现高效、流畅的异步加载机制。 一、异步加载原理 异步加载的基本思想是将耗时的操作(如网络请求和图片...
"Android实现ListView异步加载图片" Android 实现 ListView 异步加载图片是一种常见的技术,旨在提高应用程序的性能和用户体验。本文将详细介绍 Android 中实现 ListView 异步加载图片的方法,并对相关的技术概念...
本文将深入探讨如何实现ListView中图片的异步加载,并结合进度条显示加载状态,使代码更加规范。 一、异步加载图片的重要性 在Android中,如果直接在主线程中加载大图或数量众多的图片,会导致UI阻塞,用户体验...
在“ListView异步加载网络图片之一”这篇博文中,作者可能讨论了以下几点: 1. **异步加载原理**:使用AsyncTask、Handler、Thread、Runnable或者第三方库如Volley、Picasso、Glide等来在后台线程下载图片,下载...
这里的“android listView 异步加载图片”指的是在不阻塞UI线程的情况下,从网络、本地存储或其他来源加载图片到ListView中的技术。 这篇名为“ImageLoader”的Java文件很可能就是一个实现图片异步加载的工具类。在...
为了解决这个问题,我们需要采用异步加载策略,并合理管理内存,这就是“listview异步加载图片”的核心概念。 异步加载图片的基本思路是在后台线程下载和处理图片,然后在UI线程更新ListView的项。这样可以避免主线...
为了解决这个问题,我们需要实现ListView的图片异步加载。本文将深入探讨Android ListView中异步加载图片的策略、常见问题以及解决方案。 首先,我们要理解异步加载的基本原理。异步加载是指在后台线程中执行耗时...
在本压缩包文件"listview异步加载.rar"中,我们很可能会找到关于如何实现ListView异步加载的相关资料。 在Android中,ListView的异步加载通常涉及以下几个关键知识点: 1. **Adapter**:Adapter是连接ListView与...
本实例将详细讲解如何实现“Android ListView异步加载图片”,结合线程池、数据库和本地保存来优化性能。 首先,我们需要理解异步加载的概念。在Android中,由于主线程负责用户界面的更新,因此不应在主线程中执行...
Android 异步加载图片,对ListView的异步加载图片的功能演示,主要根据url读取图片返回流的方法。为了方便演示,将请求图片的链接先固定,每读取好一个图片就更新,界面比较简单,当然你可以做成比较好的,像很多好...
本文实例讲述了Android实现Listview异步加载网络图片并动态更新的方法。分享给大家供大家参考,具体如下: 应用实例:解析后台返回的数据,把每条都显示在ListView中,包括活动图片、店名、活动详情、地址、电话和...
本文将详细讲解如何在ListView中实现异步加载图片以及三级缓存的原理。 ### 一、异步加载图片的必要性 1. **避免阻塞主线程**:Android应用的UI更新必须在主线程进行,而图片加载通常需要较长的时间,如果在主线程...
这篇教程"ListView异步加载网络大图Demo"主要讲解如何高效地在ListView中异步加载网络图片,以避免性能问题。 首先,我们需要理解Bitmap对象在Android中的内存管理。Bitmap是Android用来存储像素信息的类,占用大量...
在Android应用开发中,图片异步加载是一种常见的优化技术,特别是在构建类似照片墙或ListView这样的大量图片展示场景中。这个话题主要关注如何有效地处理图片资源,避免UI阻塞,提高用户体验。以下是对"图片异步加载...