ImageLoader
来源:互联网 发布:软件测试管理体系 编辑:程序博客网 时间:2024/05/16 14:26
这次做一个图片加载器,里面涉及到线程池,bitmap的高效加载,LruCache,DiskLruCache。接下来我先介绍这四个知识点
一.线程池
优点:
(1)重用线程池中的线程,避免因为线程的创建和销毁带来性能上的开销
(2)有效控制线程池的最大并发数,避免大量线程之间因互相抢占系统资源而阻塞
(3)对线程进行简单管理,并提供定时执行和指定间隔循环执行等功能
1.ThreadPoolExecutor介绍
是线程池的真正实现,构造方法
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
corePoolSize:核心线程数
maximumPoolSize:最大线程数。超过将阻塞
keepAliveTime:非核心线程超时时长,超过将会被回收
unit:指定keepAliveTime的时间单位。常用TimeUnit.SECONDS(秒),TimeUnit.MINUTES(分钟)等
workQueue:存储线程的队列。通过ThreadPoolExecutor.execute方法提交的Runnable对象会存储在这个线程中
threadFactory:是一个接口,提供创建新线程的功能。只有一个方法:public Thread newThread(Runnable r)
2.ThreadPoolExecutor典型配置
/** * 线程池,用来管理线程 */ private static final ThreadFactory sThreadFactory = new ThreadFactory() { // AtomicInteger,一个提供原子操作的Integer的类。 private final AtomicInteger mCount = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { return new Thread(r, "ImageLoader#" + mCount.getAndIncrement()); } }; private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAX_POOL_SIZE = CPU_COUNT * 2 + 1; private static final long KEEP_ALIVE = 10L; public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(), sThreadFactory);
二.bitmap高效加载
通过BitmapFactory.Options来加载所需尺寸的图片,主要是用到了inSampleSize参数,即采样率
流程如下:
public Bitmap decodeSampleBitmapFromResource(Resources resources, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 获取options BitmapFactory.decodeResource(resources, resId, options); // 结合目标view所需大小计算采样率 options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(resources, resId, options); } /** * 指定输出图片的缩放比例 * * @param options * @param reqWidth * @param reqHeight * @return */ public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // 获得原始图片的宽高 int imageHeight = options.outHeight; int imageWidth = options.outWidth; int inSimpleSize = 1; if (imageHeight > reqHeight || imageWidth > reqWidth) { int halfHeight = imageHeight / 2; int halfWidth = imageWidth / 2; while ((halfHeight / inSimpleSize) >= reqHeight && (halfWidth / inSimpleSize) >= reqWidth) { inSimpleSize *= 2; Log.e("SimpleSize", inSimpleSize + ""); } } Log.e("inSimpleSize", inSimpleSize + ""); return inSimpleSize; }
三.LruCache(内存缓存)
Lru就是Least Recently Used近期最少使用算法。核心思想:当缓存满时,优先淘汰近期最少使用的缓存对象
先看源码:(推荐使用supprt-v4包中的LruCache,地址在E:\adt\sdk\sources\android-19\android\support\v4\util)
public class LruCache<K, V> { //map用来存储外界的缓存对象 private final LinkedHashMap<K, V> map; /** * @param maxSize for caches that do not override {@link #sizeOf}, this is * the maximum number of entries in the cache. For all other caches, * this is the maximum sum of the sizes of the entries in this cache. */ public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; this.map = new LinkedHashMap<K, V>(0, 0.75f, true); } //获取一个缓存对象 public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) { mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; } missCount++; } /* * Attempt to create a value. This may take a long time, and the map * may be different when create() returns. If a conflicting value was * added to the map while create() was working, we leave that value in * the map and release the created value. */ V createdValue = create(key); if (createdValue == null) { return null; } synchronized (this) { createCount++; mapValue = map.put(key, createdValue); if (mapValue != null) { // There was a conflict so undo that last put map.put(key, mapValue); } else { size += safeSizeOf(key, createdValue); } } if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { trimToSize(maxSize); return createdValue; } } //添加一个缓存对象 public final V put(K key, V value) { if (key == null || value == null) { throw new NullPointerException("key == null || value == null"); } V previous; synchronized (this) { putCount++; size += safeSizeOf(key, value); previous = map.put(key, value); if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, value); } trimToSize(maxSize); return previous; } /** * Remove the eldest entries until the total of remaining entries is at or * below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */ public void trimToSize(int maxSize) { while (true) { K key; V value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } if (size <= maxSize || map.isEmpty()) { break; } Map.Entry<K, V> toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } } //移除一个缓存对象,并且减少该对象对应的size public final V remove(K key) { if (key == null) { throw new NullPointerException("key == null"); } V previous; synchronized (this) { previous = map.remove(key); if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, null); } return previous; } //生成一个null对象 protected V create(K key) { return null; } //返回一个缓存对象的副本 public synchronized final Map<K, V> snapshot() { return new LinkedHashMap<K, V>(map); }}
研究完了源码,使用起来就方便了
初始化: final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap value) { return value.getByteCount() / 1024; } };获取缓存:mMemoryCache.get(key)添加缓存:mMemoryCache.put(key, bitmap)
四.DiskLruCache(磁盘缓存)
书上说地址在:https://android.googlesource.com/platform/libcore/+/android-4.1.1_r1/luni/src/main/java/libcore/io/DiskLruCache.java
但是下载下来好像要改好多东西,所以我就在Universal-ImageLoader里面找了相同的文件
1.DiskLruCache的创建
public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)
directory:存储路径
appVersion:通常为1
valueCount:单个节点对应数据个数,通常为1
maxSize:缓存总大小,比如50MB
// 初始化DiskLruCache File diskCacheDir = getDiskCacheDir(mContext, "bitmap"); if (!diskCacheDir.exists()) { diskCacheDir.mkdirs(); } if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) { try { mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE); } catch (IOException e) { e.printStackTrace(); } }
getDiskCacheDir:获取磁盘缓存目录
getUsableSpace:获取sd卡的大小和剩余空间
这两个函数的实现方法在代码包里面有,就不细说
2.DiskLruCache缓存的添加
缓存的添加是通过Editor来完成的。Editor表示缓存对象的编辑对象
//将uri转化为key String key = hashKeyForURI(uri); try { DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor .newOutputStream(DISK_CACHE_INDEX); if (downloadURIToStream(uri, outputStream)) { editor.commit(); } else { editor.abort(); } mDiskLruCache.flush(); } } catch (IOException e) { return null; }
上面的代码就是将图片写入文件系统,接下来就可以从文件系统中获取图片
解释几点
1.为什么要将uri转化为hashKey?如果uri中含有特殊字符会影响uri的使用2.downloadURIToStream实现了“把图片写入到文件系统”的功能,确切的来说,还要配合editor.commit
3.DiskLruCache缓存的查找
String key = hashKeyForURI(uri); try { DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { FileInputStream fileInputStream = (FileInputStream) snapshot .getInputStream(DISK_CACHE_INDEX); FileDescriptor fileDescriptor = fileInputStream.getFD(); bitmap = mImageResizer.decodeSampleBitmapFromFileDescriptor( fileDescriptor, width, height); if (bitmap != null) { addBitmapToMemoryCache(key, bitmap); } } } catch (IOException e) { e.printStackTrace(); }
注意,这里的Snapshot和LruCache的Snapshot不一样。LruCache的Snapshot表示内存缓存的副本,这里的Snapshot仅仅指保存了三个参数的一个对象
至此,ImageLoader已经大体实现。
代码包里面SquareImageView.java是为了得到一个宽高相同的ImageView。
同时,为了优化列表的卡顿现象,我们采用了“仅当列表静止时才加载图片”的策略
public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == OnScrollListener.SCROLL_STATE_IDLE) { mIsGridViewIdle = true; adapter.notifyDataSetChanged(); } else { mIsGridViewIdle = false; } } 在getView里面添加如下代码 if (mIsGridViewIdle) { imageLoader.bindBitmap(uri, imageView); }
运行截图
loadBitmapFromHttp和downloadBitmapFromURI都可以实现网络加载。前者是先放到disk中,然后获取,后者是先获取,然后放到memorycache中
我先把downloadBitmapFromURI注释掉
然后把loadBitmapFromHttp注释掉
代码地址:http://download.csdn.net/detail/lxj1137800599/9628778
- ImageLoader
- ImageLoader
- ImageLoader
- ImageLoader
- ImageLoader
- imageloader
- ImageLoader
- ImageLoader
- imageLoader
- ImageLoader
- ImageLoader
- ImageLoader
- ImageLoader
- ImageLoader
- imageloader
- ImageLoader
- ImageLoader
- ImageLoader
- Linux命令(17):which
- 数据加密和解密
- 【JZOJ 4770】 闭门造车(平面最近点对)
- HDU 5877 Weak Pair(dfs + 树状数组 + 离散化)
- 7.22 T hdu1102 Constructing Roads
- ImageLoader
- innerText和innerHtml的区别
- 2016 ICPC 大连网络赛 HDU 5873 Football Games
- javaEE cookie保存中文时报错
- 华为交换机堆叠和集群配置
- 将本地代码上传到github
- Andorid中UI控件的详细介绍(四)——EditText
- 16读取通话记录
- 放肆的使用UIBezierPath和CAShapeLayer画各种图形(含仿微信视频眼镜Demo)