一个自己的图片下载库--NeacyImageLoader
2016-02-17 15:44
453 查看
最近一直在和LruCache、DisLruCache、Bitmap打交道,了解了很多知识点,所以总结了写了个图片下载库,可以更直观深刻的体会其中。想给取个优雅的名字想不起来所以就叫NeacyImageLoader体验地址:https://github.com/Neacy/NeacyImageLoader
第一步,构建下载图片所需要的变量参数。
我们所需要的的两个参数分别是
然后我们需要给这两个变量定义一些配置常量,比如说缓存空间地址,缓存空间大小,以及会用到的线程池配置等等。
完成以上常量定义之后我们开始初始化LruCache和DiskLruCache两个缓存变量,并且我们整个Demo中缓存的是Bitmap对象。
初始化LruCache:
初始化DiskLruCache:
缓存对象肯定存在读取操作 我们我们也把读写操作写出来,这样在后面的Bitmap处理肯定会用到的。
Ok,完成上面的缓存配置和读取功能后我们这个时候就要考虑高效的显示Bitmap问题了。
第二,高效显示Bitmap问题
我们先定义了这么一个set
做什么呢?用于存放当LruCache中的Bitmap由于缓存空间满了被移除的Bitmap然后在Options.inBitmap中重复利用起来,提高了整体的性能。其中的比对方式如是:
所以我们在压缩裁剪的时候调用这个方法就好了。
所以一个完整的图片压缩裁剪的方法就完成了:
其中caculateInsampleSize方法用以获取inSampleSize方法太多这里就不贴出来了。
第三、开始处理获取Bitmap
我们都是先从LruCache中先读取有的话就直接显示出来,没有的话读取DiskLruCache,最后再没有读取网络下载图片。
其中getBitmapFromHttp这个方法就是纯粹的从网络下载数据并写入DiskLruCache中去。
我们将网络得到的Bitmap存入磁盘之后 再根据DisLruCache方法读取即可。然后我们把Bitmap封装到LoadResult类,使用Handler回调到主线程供ImageView显示,Handler中实现的代码片段:
当然其中我们还使用到了线程池
最后,还处理了下快速滑动的时候暂停线程加载,这样整体的性能表现的更优。用到的方法就是同步所的wait和notifyAll配对使用。
快速滑动的时候暂停操作:
然后对GridView配置监听快速滑动事件:
上面就是这个简单的图片下载库代码逻辑。
然后在Activity中使用只要按如下方式就好了。
通过自己动手写了个简易的下载库,对世面常见的高级下载库就能更熟悉他们的套路了。
第一步,构建下载图片所需要的变量参数。
我们所需要的的两个参数分别是
private LruCache<String, Bitmap> mLruCache; private DiskLruCache mDiskLruCache;
然后我们需要给这两个变量定义一些配置常量,比如说缓存空间地址,缓存空间大小,以及会用到的线程池配置等等。
/** * 缓存空间大小 */ private static final int CACHE_DIS_SIZE = 250 * 1024 * 1024;// 250M private int CACHE_MEM_SIZE = (int) (Runtime.getRuntime().maxMemory() / 1024 / 8);// kb为单位 private static final String DIR_DISK_NAME = "image_cache"; private static final int IMAGE_TAG_KEY = R.id.neacy_imageloader_id; /** * 为线程池配置常量参数 */ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_THREAD_SIZE = CPU_COUNT + 1; private static final int CORE_THREAD_MAX_SIZE = CPU_COUNT * 2 + 1; private static final int THREAD_TIME = 10;
完成以上常量定义之后我们开始初始化LruCache和DiskLruCache两个缓存变量,并且我们整个Demo中缓存的是Bitmap对象。
初始化LruCache:
private void initMemoryCache() { mLruCache = new LruCache<String, Bitmap>(CACHE_MEM_SIZE) { @Override protected int sizeOf(String key, Bitmap value) { return (value.getRowBytes() * value.getHeight()) / 1024;// kb为单位 } @Override protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) { super.entryRemoved(evicted, key, oldValue, newValue); mEntryBitmaps.add(new SoftReference<Bitmap>(oldValue)); } }; }
初始化DiskLruCache:
private void initDiskCache(Context context) { File dir = new File(context.getExternalCacheDir().getPath() + File.separator + DIR_DISK_NAME); if (!dir.exists()) { dir.mkdirs(); } try { if (null == mDiskLruCache || mDiskLruCache.isClosed()) { mDiskLruCache = DiskLruCache.open(dir, 1, 1, CACHE_DIS_SIZE); } } catch (IOException e) { e.printStackTrace(); } }
缓存对象肯定存在读取操作 我们我们也把读写操作写出来,这样在后面的Bitmap处理肯定会用到的。
/** * 写入缓存 */ public void addBitmaptoCache(String url, Bitmap bitmap) { if (TextUtils.isEmpty(url) || bitmap == null) { return; } if (null != mLruCache) { mLruCache.put(url, bitmap); } } /** * 从LruCache中读取缓存 */ public Bitmap getBitmapFromMemCache(String url) { if (TextUtils.isEmpty(url) || mLruCache == null) return null; return mLruCache.get(url); } /** * 从DiskLruCache中读取缓存 */ public Bitmap getBitmapFromDiskCache(String url) { if (TextUtils.isEmpty(url) || mDiskLruCache == null) return null; FileInputStream is = null; Bitmap bitmap = null; try { DiskLruCache.Snapshot snapshot = mDiskLruCache.get(url); if (null != snapshot) { is = (FileInputStream) snapshot.getInputStream(0); // 压缩图片 bitmap = ImageResizer.decodeBitmapFromFileDescriptor(is.getFD(), reqWidth, reqHeight, this); } } catch (IOException e) { e.printStackTrace(); } finally { LoadUtil.closeInputSream(is); } return bitmap; }
Ok,完成上面的缓存配置和读取功能后我们这个时候就要考虑高效的显示Bitmap问题了。
第二,高效显示Bitmap问题
我们先定义了这么一个set
private Set<SoftReference<Bitmap>> mEntryBitmaps = new HashSet<SoftReference<Bitmap>>();
做什么呢?用于存放当LruCache中的Bitmap由于缓存空间满了被移除的Bitmap然后在Options.inBitmap中重复利用起来,提高了整体的性能。其中的比对方式如是:
/** * 读取inBitmap */ public Bitmap getInBitmap(BitmapFactory.Options option) { Bitmap mResultBitmap = null; if (!mEntryBitmaps.isEmpty()) { Iterator<SoftReference<Bitmap>> it = mEntryBitmaps.iterator(); Bitmap item; while(it.hasNext()) { item = it.next().get(); if (item != null && item.isMutable()) { if (isCanReuseinBitmap(item, option)) { mResultBitmap = item; it.remove(); break; } } else { it.remove(); } } } return mResultBitmap; } private boolean isCanReuseinBitmap(Bitmap bitmap, BitmapFactory.Options option) { int width = option.outWidth / option.inSampleSize; int heitht = option.outHeight / option.inSampleSize; return bitmap.getWidth() == width && bitmap.getHeight() == heitht; }
所以我们在压缩裁剪的时候调用这个方法就好了。
public static void getCanUseInBitmap(ImageLoader imageLoader, BitmapFactory.Options options) { options.inMutable = true; Bitmap inBitmap = imageLoader.getInBitmap(options); if (null != inBitmap) { options.inBitmap = inBitmap; } return; }
所以一个完整的图片压缩裁剪的方法就完成了:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fd, null, options); options.inSampleSize = caculateInsampleSize(options, reqWidth, reqHeight); options.inJustDecodeBounds = false; getCanUseInBitmap(imageLoader, options); return BitmapFactory.decodeFileDescriptor(fd, null, options);
其中caculateInsampleSize方法用以获取inSampleSize方法太多这里就不贴出来了。
第三、开始处理获取Bitmap
我们都是先从LruCache中先读取有的话就直接显示出来,没有的话读取DiskLruCache,最后再没有读取网络下载图片。
public void loadImage(final String url, final ImageView imageView) { imageView.setTag(IMAGE_TAG_KEY, url); final String key = LoadUtil.hashKeyForDisk(url); final Bitmap bitmap = getBitmapFromMemCache(key); if (null != bitmap) { Log.w("Jayuchou", "--- load image from memory ---"); imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(R.mipmap.ic_launcher); // 开启线程 先读取磁盘中的图片 再没有数据读取网络的数据 Runnable mLoadRunnable = new Runnable() { @Override public void run() { // 快速滑动的时候暂停操作 synchronized (mPauseLock) { Log.w("Jayuchou", "---isPauseWork out = " + isPauseWork); while (isPauseWork) { Log.w("Jayuchou", "---isPauseWork = " + isPauseWork); try { mPauseLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } Bitmap bmp = getBitmapFromDiskCache(key); if (null == bmp) { Log.w("Jayuchou", "--- load image from net ---"); try { DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (null != editor) { OutputStream os = editor.newOutputStream(0); if (getBitmapFromHttp(url, os)) { editor.commit(); bmp = getBitmapFromDiskCache(key); addBitmaptoCache(key, bmp);// 加入缓存中 } else { editor.abort(); } } } catch (IOException e) { e.printStackTrace(); } } else { Log.w("Jayuchou", "--- load image from disk ---"); addBitmaptoCache(key, bmp);// 加入缓存中 } LoadResult result = new LoadResult(url, bmp, imageView); mMainHandler.obtainMessage(0, result).sendToTarget(); } }; mImageLoaderExecutor.execute(mLoadRunnable); } }
其中getBitmapFromHttp这个方法就是纯粹的从网络下载数据并写入DiskLruCache中去。
我们将网络得到的Bitmap存入磁盘之后 再根据DisLruCache方法读取即可。然后我们把Bitmap封装到LoadResult类,使用Handler回调到主线程供ImageView显示,Handler中实现的代码片段:
public void handleMessage(Message msg) { super.handleMessage(msg); LoadResult result = (LoadResult) msg.obj; ImageView imageView = result.getImageView(); String key = (String) imageView.getTag(IMAGE_TAG_KEY); if (key.equals(result.getUrl())) { imageView.setImageBitmap(result.getBitmap()); } }
当然其中我们还使用到了线程池
private static final Executor mImageLoaderExecutor = new ThreadPoolExecutor(CORE_THREAD_SIZE, CORE_THREAD_MAX_SIZE, THREAD_TIME, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), mThreadFactory);
最后,还处理了下快速滑动的时候暂停线程加载,这样整体的性能表现的更优。用到的方法就是同步所的wait和notifyAll配对使用。
public void setPauseWork(boolean isPauseWork) { synchronized (mPauseLock) { this.isPauseWork = isPauseWork; if (!isPauseWork) {// 当快速滑动停止的时候通知线程释放暂停操作 mPauseLock.notifyAll(); } } }
快速滑动的时候暂停操作:
// 快速滑动的时候暂停操作 synchronized (mPauseLock) { while (isPauseWork) { try { mPauseLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } }
然后对GridView配置监听快速滑动事件:
mGridView.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case SCROLL_STATE_IDLE:// 快速滑动或者手指移开屏幕的时候继续加载 imageLoader.setPauseWork(false); break; case SCROLL_STATE_TOUCH_SCROLL: break; case SCROLL_STATE_FLING:// 手指快速滑动的时候暂停图片加载提高性能 imageLoader.setPauseWork(true); break; } }
上面就是这个简单的图片下载库代码逻辑。
然后在Activity中使用只要按如下方式就好了。
imageLoader = new ImageLoader(getApplicationContext()); imageLoader.setReqWidthAndHeight(200, 200); imageLoader.loadImage(Images.imageUrls[position], holder.image);
通过自己动手写了个简易的下载库,对世面常见的高级下载库就能更熟悉他们的套路了。
相关文章推荐
- 磁盘缓存专题之一 缓存命中和缓存未命中&缓存与缓冲间的差异
- php让图片可以下载的代码第1/2页
- Python3实现Web网页图片下载
- 收藏各种技术源码
- android 图片下载方法总结
- android listview 异步加载图片并防止错位
- LruMemoryCache和LruCache
- Android-我自己的网络图片下载缓存类
- Android异步加载全解析之引入一级缓存
- 安卓图片下载
- Leetcode: LRU Cache
- Android提供的LruCache类简介
- Android高效加载大图、多图解决方案,有效避免程序OOM
- Android照片墙完整版,完美结合LruCache和DiskLruCache
- Android 网络通信框架Volley简介(Google IO 2013)
- SDWebImage
- Android LruCache 缓存 类 源码 注解 分析
- 读《Android开发艺术探索》----ImageLoader的实现
- Android中的LruCache缓存技术
- LruCache图片缓存技术应用(图片浏览器)