ListView异步加载网络图片之三

[align=center][size=large][b]ListView异步加载网络图片之完美解决方案[/b][/size][/align]
[size=large][b]问题描述:[/b]上一篇文章中解决了一个图片显示混乱的bug,但是还遗留下来一个更严重的bug,那就是当我们猛地拖动列表的时候,会感觉非常的卡顿,并且继续不顾一切的拖动程序就会崩溃,看一下抛出的异常是[color=red]RejectedExecutionException[/color]。[/size]

[img]http://dl.iteye.com/upload/attachment/0074/6624/a02fb780-d3d2-3654-9b90-87aec24b1a54.png[/img]
[size=large][b]错误的原因:[/b]由于异步加载图片用的是AsyncTask(异步任务),AsyncTask的内容实现是采用的一个线程池,池子的的最大容量是128,然而连续的滚动ListView触发了getView的调用,在getView中又去创建并执行了异步任务,所以就会导致线程池满了,看一下AsyncTask的源码[/size]

//池子的最大容量
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);

[size=large]从AysncTask的execute方法中调用线程数的方法[color=red]sExecutor.execute(mFuture)[/color],下面就是线程池的execute方法,在这个方法中由于线程池满了,所以命名会被拒绝执行也就会调用[color=red]reject(command)[/color] 最终将会抛出RejectedExecutionException异常。[/size]

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);
}

[size=large][b]追踪到抛出异常的代码是在AbortPolicy类[/b],AbortPolicy实现了RejectedExecutionHandler接口因此实现了rejectedExecution方法[/size]

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
}

[size=large][b]解决的方法:[/b]产生问题的原因是执行了太多的异步任务,所以解决问题的方法就是,减少异步任务的创建,那么如何减少呢?我们知道ListView在的滚动有3个状态
[color=red][list]
[*]SCROLL_STATE_FLING(惯性滚动)
[*]SCROLL_STATE_IDLE(空闲)
[*]SCROLL_STATE_TOUCH_SCROLL(拖动)
[/list] [/color] 当ListView在处于SCROLL_STATE_FLING状态的时候由于是快速的从屏幕上面飞过,其实没有必要创建异步任务下载图片,只有当ListView处于低速的拖动状态或者是空闲状态的时候才去创建异步任务,这样就会大大的降低异步任务的数量。
那么可以在adapter中通过一个变量[color=red]mBusy[/color]来控制adapter的状态当[color=red]mBusy==false的时候才去创建异步任务[/color],并且我们给ListView设置一个[color=red]setOnScrollListener,在监听器的回调函数中改变adapte的mBusy的值,达到控制是否创建异步任务的作用[/color][/size]

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;
}

[size=large]总结:本文对前一个demo遗留的bug做了讲解并解决,在此基础上还对程序做了进一步的优化,这次的demo通过双缓冲技术对数据进行缓存,代码已经上传,同学们可以自己下载下来研究一下双缓冲技术。[color=red]我在下一篇博客中会对双缓冲的代码进行详细的分析[/color],谢谢您看到了博客的结尾[/size]。 :arrow:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值