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

    以下内容为博主阅读《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);

		@Override
		public 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()) {
		@Override
		public 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() {

			@Override
			public 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);
			// 写入成功则返回true
			if (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相关的文件描述对象fileDescriptor
				FileDescriptor 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) {
			@Override
			protected 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
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值