笔记---ImageLoader实现图片压缩,缓存

来源:互联网 发布:java工程师培训 编辑:程序博客网 时间:2024/05/22 16:48
    以下内容为博主阅读《Android开发艺术探索》所记代码以及知识点的梳理
package com.example.utils;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.IOException;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.concurrent.Executor;import java.util.concurrent.LinkedBlockingDeque;import java.util.concurrent.ThreadFactory;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Build;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.os.StatFs;import android.os.Build.VERSION_CODES;import android.os.Environment;import android.support.v4.util.LruCache;import android.util.Log;import android.widget.ImageView;public class ImageLoader {private static final String TAG = "ImageLoader";public static final int MESSAGE_POST_RESULT = 1;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 int IO_BUFFER_SIZE = 8 * 1024;private static final int DISK_CACHE_INDEX = 0;private boolean mIsDiskLruCacheCreated = false;private Context mContext;private DiskLruCache mDiskLruCache;private static final int TAG_KEY_URI = 100;private ImageResizer mImageResizer = new ImageResizer();private LruCache<String, Bitmap> mMemoryCache;private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50;// 创建线程工厂,使用原子类保证线程安全private static final ThreadFactory sThreadFactory = new ThreadFactory() {// 内部被volatile修饰,线程安全private final AtomicInteger mCount = new AtomicInteger(1);@Overridepublic Thread newThread(Runnable r) {// 执行任务r,线程名"ImageLoader#"+mCount.getAndIncrement()return new Thread(r, "ImageLoader#" + mCount.getAndIncrement());}};// 线程池(核心线程,最大线程,待机时间单位,工作队列,线程工厂)public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>(), sThreadFactory);private Handler mMainHandler = new Handler(Looper.getMainLooper()) {@Overridepublic void handleMessage(Message msg) {LoaderResult result = (LoaderResult) msg.obj;ImageView imageView = result.imageView;// imageView.setImageBitmap(result.bitmap);(书中多了此段代码,估计为纰漏)String uri = (String) imageView.getTag(TAG_KEY_URI);//解决快速滑动时imageView复用,uri变化而使得图片错位if (uri.equals(result.uri)) {imageView.setImageBitmap(result.bitmap);} else {Log.w(TAG, "url is change,ignored!");}};};// 异步加载接口设计public void bindBitmap(final String uri, final ImageView imageView,final int reqWidth, final int reqHeight) {imageView.setTag(TAG_KEY_URI, uri);Bitmap bitmap = loadBitmapFromMemCache(uri);if (bitmap != null) {imageView.setImageBitmap(bitmap);return;}Runnable loadBitmapTask = new Runnable() {@Overridepublic void run() {Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight);if (bitmap != null) {LoaderResult result = new LoaderResult(imageView, uri,bitmap);// obtainMessage().sendToTarget()和sendMessage()区别在于前者是从MessagePool拿的,后者是自己创建的mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget();}}};THREAD_POOL_EXECUTOR.execute(loadBitmapTask);}// 同步加载接口设计protected Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) {Bitmap bitmap = loadBitmapFromMemCache(uri);if (bitmap != null) {return bitmap;}bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight);if (bitmap != null) {return bitmap;}try {bitmap = loadBitmapFromHttp(uri, reqWidth, reqWidth);} catch (IOException e) {e.printStackTrace();}if (bitmap == null && !mIsDiskLruCacheCreated) {bitmap = downloadBitmapFromUrl(uri);}return bitmap;}// 从网络加载bitmap数据private Bitmap downloadBitmapFromUrl(String uri) {Bitmap bitmap = null;HttpURLConnection urlConnection = null;BufferedInputStream is = null;try {URL url = new URL(uri);urlConnection = (HttpURLConnection) url.openConnection();is = new BufferedInputStream(urlConnection.getInputStream(),IO_BUFFER_SIZE);bitmap = BitmapFactory.decodeStream(is);} catch (IOException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}IOutil.close(is);}return bitmap;}// 从网络加载bitmap数据,并将其写入到磁盘缓存private Bitmap loadBitmapFromHttp(String uri, int reqWidth, int reqHeight)throws IOException {if (Looper.myLooper() == Looper.getMainLooper()) {throw new RuntimeException("can not visit network from UI Thread.");}String key = hashKeyFormUrl(uri);// 磁盘缓存写入DiskLruCache.Editor editor = mDiskLruCache.edit(key);if (editor != null) {// 磁盘缓存输出流OutputStream os = editor.newOutputStream(DISK_CACHE_INDEX);// 写入成功则返回trueif (downloadUrlToStream(uri, os)) {// 提交editor.commit();} else {// 撤销editor.abort();}mDiskLruCache.flush();}return loadBitmapFromDiskCache(uri, reqWidth, reqHeight);}private boolean downloadUrlToStream(String uri, OutputStream os) {HttpURLConnection urlConnection = null;BufferedOutputStream out = null;BufferedInputStream in = null;try {URL url = new URL(uri);urlConnection = (HttpURLConnection) url.openConnection();in = new BufferedInputStream(urlConnection.getInputStream());out = new BufferedOutputStream(os, IO_BUFFER_SIZE);int length = 0;while ((length = in.read()) != -1) {out.write(length);}return true;} catch (IOException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}IOutil.close(out);IOutil.close(in);}return false;}// 加载磁盘缓存中的图片private Bitmap loadBitmapFromDiskCache(String uri, int reqWidth,int reqHeight) {if (Looper.getMainLooper() == Looper.myLooper()) {Log.w(TAG, "load bitmap from UI Thread,it's not recommended!");}if (mDiskLruCache == null) {return null;}Bitmap bitmap = null;String key = hashKeyFormUrl(uri);try {DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);if (snapshot != null) {FileInputStream fis = (FileInputStream) snapshot.getInputStream(DISK_CACHE_INDEX);// 获取该文件输入流fis相关的文件描述对象fileDescriptorFileDescriptor fileDescriptor = fis.getFD();bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight);if (bitmap != null) {//成功获取磁盘缓存的图片后将其添加到内存缓存addBitmapToMemoryCache(key, bitmap);}}} catch (IOException e) {e.printStackTrace();}return bitmap;}// 加载内存缓存中的图片private Bitmap loadBitmapFromMemCache(String uri) {final String key = hashKeyFormUrl(uri);Bitmap bitmap = getBitmapFromMemoryCache(key);return bitmap;}// 私有构造方法,初始化成员变量private ImageLoader(Context context) {mContext = context;int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);int cacheMemory = maxMemory / 8;mMemoryCache = new LruCache<String, Bitmap>(cacheMemory) {@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight() / 1024;}};File diskCacheDir = getDiskCacheDir(context, "bitmap");if (diskCacheDir.exists()) {diskCacheDir.mkdirs();}if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) {try {mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1,DISK_CACHE_SIZE);mIsDiskLruCacheCreated = true;} catch (IOException e) {e.printStackTrace();}}}// 往内存缓存中添加bitmap数据private void addBitmapToMemoryCache(String key, Bitmap bitmap) {if (getBitmapFromMemoryCache(key) == null) {mMemoryCache.put(key, bitmap);}}// 从内存缓存中获取Bitmap对象private Bitmap getBitmapFromMemoryCache(String key) {return mMemoryCache.get(key);}// 返回ImageLoader对象public static ImageLoader build(Context context) {return new ImageLoader(context);}// 获取文件的可用空间private long getUsableSpace(File path) {// 判断SDK版本是否大于姜饼版本(2.3版本)if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) {return path.getUsableSpace();}// 低版本无法使用上述方法,防止报错final StatFs stats = new StatFs(path.getPath());return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks();}// 获取磁盘缓存的路径private File getDiskCacheDir(Context context, String uniqueName) {// 判断SD卡是否存在并且没有被移除boolean externalStorageAvailable = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);final String cachePath;if (externalStorageAvailable) {cachePath = context.getExternalCacheDir().getPath();} else {cachePath = context.getCacheDir().getPath();}// 路径+“\”+文件名return new File(cachePath + File.separator + uniqueName);}private String bytesToHexString(byte[] bytes) {StringBuilder sb = new StringBuilder();for (int i = 0; i < bytes.length; i++) {// 将字节数组转换为字符数组,清除高位二十四比特(byte为8位,int为32位)String hex = Integer.toHexString(0xFF & bytes[i]);if (hex.length() == 1) {sb.append('0');}sb.append(hex);}return sb.toString();}// 给url地址进行MD5加密private String hashKeyFormUrl(String url) {String cacheKey;try {final MessageDigest mDigest = MessageDigest.getInstance("MD5");mDigest.update(url.getBytes());cacheKey = bytesToHexString(mDigest.digest());} catch (NoSuchAlgorithmException e) {cacheKey = String.valueOf(url.hashCode());}return cacheKey;}// 将imageView,url,bitmap封装成实体类,以便Handler传输数据private static class LoaderResult {public ImageView imageView;public String uri;public Bitmap bitmap;public LoaderResult(ImageView imageView, String uri, Bitmap bitmap) {this.imageView = imageView;this.uri = uri;this.bitmap = bitmap;}}}

实现功能:图片的同步加载、图片的异步加载、图片压缩、内存缓存、磁盘缓存、网络垃圾

详情见图片和代码注释



2 0
原创粉丝点击