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 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(), 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(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;
}
}
}
实现功能:图片的同步加载、图片的异步加载、图片压缩、内存缓存、磁盘缓存、网络垃圾
详情见图片和代码注释