FindJpg(3)-图片加载中的线程池应用
来源:互联网 发布:淘宝 i7主机 编辑:程序博客网 时间:2024/06/05 04:14
一、线程池的优点:
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
- 能有效控制线程池的最大并发数量,避免大量线程间因相互抢占系统资源而导致阻塞。
- 能够对线程进行简单的管理,提供定时执行、指定间隔循环执行等功能。
二、ThreadPoolExecutor:
- TreadPoolExecutor是线程池的真正实现,它的构造方法提供了一系列参数来配置线程池。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
- corePoolSize:核心线程数,默认情况下会在线程池中一直存活,即使它们处于闲置状态。
- maximumPoolSize:线程池所能容纳的最大线程数,当活动线程数到达此数值后,后续的新任务将被阻塞。
- keepAliveTime:非核心线程闲置时的超时时长,超过次时长后非核心线程将被回收。当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程。
- unit:用于指定keepAliveTime参数的时间单位,常用的有TimeUnit.MILLSECONDS、TimeUnit.SECONDS、TimeUnit.MINUTES等。
- workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
- threadFactory:线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r){}。
- ThreadPoolExecutor执行任务时遵循的规则:
- 若线程池中的线程数量未达到corePoolSize,那么会直接启动一个核心线程来执行任务。
- 若达到或超过corePoolSize,那么认为会被插入到workQueue排队等待执行。
- 若在步骤2中无法将任务插入,往往是因为workQueue已满,这时若线程数量未达到maximumPoolSize,就会立刻启动一个非核心线程来执行任务。
- 若步骤3中达到了maximumPoolSize,那么就拒绝执行此任务,ThreadPoolExecotor会调用RejectedExecutionHandler的rejectedExecution方法来通知调用者。
三、图片加载中线程池的具体应用
- 对ThreadPoolExecutor进行参数配置(参考AsyncTask中的线程池配置情况)
代码如下:
private static final int CPU_COUNT=Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE=CPU_COUNT+1;
private static final int MAXIMUM_POOL_SIZE=CPU_COUNT*2+1;
private static final long KEEP_ALIVE=10L;
private static final ThreadFactory sThreadFactory=new ThreadFactory() {
private final AtomicInteger mCount=new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r,"ImageLoader#"+mCount.getAndIncrement());//取当前值并自增
}
};
public static final Executor THREAD_POOL_EXECUTOR=newThreadPoolExecutor(
CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,
KEEP_ALIVE,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(),sThreadFactory);
- 图片的异步加载
- 先从内存缓存中读取图片,若成功则直接返回结果。否则去线程池调用loadBitmap方法(见上篇博文),loadBitmap是同步加载的过程,一般同步加载过程(特别是有网络请求的情况)会比较耗时,所以这里放在在线程池中调用。
- 当图片加载成功后将图片、图片地址和需要绑定的imageView封装成一个LoaderResult对象,通过mMainHandler来访问UI进行UI层面的相关操作。
- 当中的一个细节是:在bindBitmap里对imageView setTag,这样在主线程中给imageView设置图片前先检查uri是否改变,这样可以解决由于View复用导致列表错位的问题。
具体代码如下:
public void bindBitmap(finalString uri,finalImageView imageView,final intreqWidth,final intreqHeight){
imageView.setTag(TAG_KEY_URI,uri);
Bitmap bitmap=loadBitmapFromMemCache(uri);
if(bitmap!=null){
imageView.setImageBitmap(bitmap);
return;
}
final Runnable loadBitmapTask=newRunnable() {
@Override
public void run() {
Bitmap bitmap=loadBitmap(uri,reqWidth,reqHeight);
if(bitmap!=null){
LoaderResult result=new LoaderResult(imageView,uri,bitmap);
mMainHandler.obtainMessage(MESSAGE_POST_RESULT,result).sendToTarget();
}
}
};
THREAD_POOL_EXECUTOR.execute(loadBitmapTask);
}
四、后记:异步操作带来的列表错位问题
- 关于列表错位的问题,一般是重用convertView和异步操作同时发生才会出现,这儿要联系getView来理解(之前的博文ListView优化详解有简单介绍)。比如Item8复用Item1的View,如果Item1的数据(比如图片)加载慢,Item8的快,当上滑使Item8可见时,Item8先显示自己的图片,但等到Item1图片也完成加载时Item8的图片变成Item1的图片了。因为它们复用的是同一个View,指向同一块内存。同理若Item1快Item8慢,上滑使Item8可见时先显示Item1的图片。因此,重用convertView和异步操作同时发生时,会出现列表错位的问题。
Demo地址:FindJpg
- FindJpg(3)-图片加载中的线程池应用
- FindJpg(2)-BitMap的高效加载和缓存
- iOS开发笔记--异步加载图片在TableView中的应用
- iOS开发笔记--异步加载图片在TableView中的应用
- 自定义ImageLoader实现图片加载线程池、图片缓存
- Android中的图片加载
- 加载图库中的图片
- 非UI线程加载图片
- Android实现ListView异步加载图片+缓存+线程池管理
- 图片加载机制——压缩,缓存,线程池
- 线程池在socket通信中的应用
- Android线程池异步加载图片,可LIFO,可FIFO加载,四级缓存
- android中的加载网络图片
- js中的图片预加载
- 图片加载及缓存框架Glide在安卓开发中的应用
- android 图片异步加载(线程池,等待,唤醒;图片缓存在内存)
- 使用Android新式LruCache缓存图片,基于线程池异步加载图片
- android异步图片加载中的图片缓存
- 手机开发实战141——图片介绍
- 算法和生活相依相存
- 手机开发实战142——RGB介绍1
- 2016.6.11初中部模拟赛总结
- php类文件命名规范
- FindJpg(3)-图片加载中的线程池应用
- 手机开发实战143——RGB介绍2
- T1: 城墙(sandcas.pas/cpp)
- [JZOJ3431]【GDOI2014模拟】网格
- android onTouch()与onTouchEvent()的区别
- 11. 配置ContextPath【从零开始学Spring Boot】
- C++考试总结
- 手机开发实战144——YUV介绍
- 手机开发实战145——ALPHA介绍