使用HandlerThread实现的加载,HandlerThread中定义了线程、调用了Looper.prepare()和Looper.loop()方法。与Handler绑定后,loop()方法循环从MessageQueue中取Handler发送来的消息进行图片加载。
直接上代码:
/**
* 加载图片核心类,可以拷贝直接用
* @Description: TODO
* @author 张永飞
* @date 2016-6-11
*
*/
public class ImageLoader {
private static ImageLoader mInstance;
// 队列调度方式: 后进先出
public static final int LIFO = 0;
// 队列调度方式: 先进先出
public static final int FIFO = 1;
private Handler mThreadHandler;
// 队列调度方式
private int mType;
// 线程池
private ExecutorService mThreadPool;
// 控制线程资源的信号量
private volatile Semaphore mPoolSemaphore;
// 任务队列链表
private LinkedList<Runnable> mTaskQueue;
private LruCache<String, Bitmap> mLruCache;
private Handler mUIHandler;
// 是否开启本地磁盘缓存
private static final boolean isDiskCacheEnable = true;
public static ImageLoader getInstance(int threadCount, int type) {
if (mInstance == null) {
synchronized (ImageLoader.class) {
if (mInstance == null) {
mInstance = new ImageLoader(threadCount, type);
}
}
}
return mInstance;
}
private ImageLoader(int threadCount, int type) {
initBackThread();
initLruCache();
mType = type;
mTaskQueue = new LinkedList<Runnable>();
mThreadPool = Executors.newFixedThreadPool(threadCount);
mPoolSemaphore = new Semaphore(threadCount);
}
private void initBackThread() {
HandlerThread handlerThread = new HandlerThread("image-loader-thread");
handlerThread.start();
mThreadHandler = new Handler(handlerThread.getLooper()) {
public void handleMessage(Message msg) {
try {
mPoolSemaphore.acquire();
mThreadPool.execute(getTask());
} catch (Exception e) {
e.printStackTrace();
}
};
};
}
private void initLruCache() {
// 进程可以从虚拟机获取的最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mLruCache = new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
// 图片的行字节数 * 图片的高度 = 图片的字节数
return value.getRowBytes() * value.getHeight();
}
};
}
/**
* 根据路径设置图片到对应的ImageView上
* @param path 图片全路径名,可以是URL
* @param imageView 显示图片的控件
* @param isFromNet 是否从网络下载
*/
public void loadImage(final String path, final ImageView imageView,
final boolean isFromNet) {
imageView.setTag(path);
if (mUIHandler == null) {
mUIHandler = new Handler() {
public void handleMessage(Message msg) {
ImageBeanHolder holder = (ImageBeanHolder) msg.obj;
ImageView imageView2 = holder.imageView;
Bitmap bitmap2 = holder.bitmap;
String path2 = holder.path;
// 防止图片错位
if (imageView2.getTag().toString().equals(path2)) {
imageView2.setImageBitmap(bitmap2);
}
};
};
}
Bitmap bitmap = getBitmapFromLruCache(path);
if (bitmap != null) {
sendMsgToUIHandler(path, imageView, bitmap);
} else {
addTask(buildTask(path, imageView, isFromNet));
}
}
private synchronized void addTask(Runnable runnable) {
mTaskQueue.add(runnable);
mThreadHandler.sendEmptyMessage(0);
}
private synchronized Runnable getTask() {
Runnable result = null;
if (mType == LIFO) {
result = mTaskQueue.removeLast();
} else if (mType == FIFO) {
result = mTaskQueue.removeFirst();
}
return result;
}
private Runnable buildTask(final String path, final ImageView imageView,
final boolean isFromNet) {
return new Runnable() {
@Override
public void run() {
Bitmap bitmap = null;
if (isFromNet) {
bitmap = loadImageFromUrl(path, imageView);
} else {
bitmap = loadImageFromLocal(path, imageView);
}
putBitmapToLruCache(path, bitmap);
sendMsgToUIHandler(path, imageView, bitmap);
mPoolSemaphore.release();
}
};
}
private Bitmap loadImageFromUrl(String path, ImageView imageView) {
File picDiskCacheFile = getPicDiskCacheFile(imageView.getContext(),
getFileMD5(path));
Bitmap bitmap = null;
if (picDiskCacheFile.exists()) {
bitmap = loadImageFromLocal(picDiskCacheFile.getAbsolutePath(),
imageView);
} else {
if (isDiskCacheEnable) {
boolean isSuccess = downloadUrlPic(path, picDiskCacheFile);
if (isSuccess) {
bitmap = loadImageFromLocal(
picDiskCacheFile.getAbsolutePath(), imageView);
} else {
System.out.println(picDiskCacheFile.getAbsolutePath()
+ "下载失败,删除");
picDiskCacheFile.delete();
}
} else {
bitmap = loadUrlBitmap(path, imageView);
}
}
return bitmap;
}
private Bitmap loadImageFromLocal(String path, ImageView imageView) {
ImageViewSize imageViewSize = getImageViewSize(imageView);
return decodeThumbnailFromResource(path, imageViewSize);
}
// 从网络下载图片
private boolean downloadUrlPic(String urlStr, File file) {
FileOutputStream fos = null;
InputStream is = null;
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = conn.getInputStream();
fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
private String getFileMD5(String path) {
try {
MessageDigest digest = MessageDigest.getInstance("md5");
byte[] bytes = digest.digest(path.getBytes());
StringBuilder result = new StringBuilder();
String tempStr = null;
for (byte b : bytes) {
tempStr = Integer.toHexString(b & 0xff);
if (tempStr.length() == 1) {
result.append("0");
}
result.append(tempStr);
}
return result.toString();
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
private File getPicDiskCacheFile(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
}
// 从网络获取用于显示在ImageView中的图片
private Bitmap loadUrlBitmap(String urlStr, ImageView imageView) {
InputStream is = null;
try {
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
is = new BufferedInputStream(conn.getInputStream());
is.mark(is.available());
Options options = new Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, options);
ImageViewSize imageViewSize = getImageViewSize(imageView);
options.inSampleSize = getInSampleSize(imageViewSize, options);
options.inJustDecodeBounds = false;
is.reset();
Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
conn.disconnect();
return bitmap;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
private void sendMsgToUIHandler(final String path,
final ImageView imageView, Bitmap bitmap) {
ImageBeanHolder holder = new ImageBeanHolder();
holder.bitmap = bitmap;
holder.imageView = imageView;
holder.path = path;
Message message = Message.obtain();
message.obj = holder;
mUIHandler.sendMessage(message);
}
private void putBitmapToLruCache(String path, Bitmap bitmap) {
if (getBitmapFromLruCache(path) == null) {
if (bitmap != null) {
mLruCache.put(path, bitmap);
}
}
}
private Bitmap getBitmapFromLruCache(String path) {
return mLruCache.get(path);
}
private Bitmap decodeThumbnailFromResource(String path,
ImageViewSize imageViewSize) {
Options options = new Options();
// 不把位图加载到内存中,只获取位图的头文件信息
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
int inSampleSize = getInSampleSize(imageViewSize, options);
options.inSampleSize = inSampleSize;
// 把位图解析到内存中
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
private int getInSampleSize(ImageViewSize imageViewSize, Options options) {
int imageViewWidth = imageViewSize.width;
int imageViewHeight = imageViewSize.height;
int picWidth = options.outWidth;
int picHeight = options.outHeight;
int inSampleSize = 1;
if (picWidth > imageViewWidth || picHeight > imageViewHeight) {
int widthScale = Math.round(picWidth * 1.0f / picHeight);
int heightScale = Math.round(picHeight * 1.0f / imageViewHeight);
inSampleSize = Math.max(widthScale, heightScale);
}
return inSampleSize;
}
private ImageViewSize getImageViewSize(ImageView imageView) {
ImageViewSize imageViewSize = new ImageViewSize();
DisplayMetrics metrics = imageView.getContext().getResources()
.getDisplayMetrics();
LayoutParams params = imageView.getLayoutParams();
int width = imageView.getWidth();// imageView的实际宽度
if (width <= 0) {
width = params.width;// layout中声明的宽度
}
if (width <= 0) {
width = imageView.getMaxWidth();// imageView的最大宽度
}
if (width <= 0) {
width = metrics.widthPixels;// 屏幕宽度
}
int height = imageView.getHeight();
if (height <= 0) {
height = params.height;
}
if (height <= 0) {
height = imageView.getMaxHeight();
}
if (height <= 0) {
height = metrics.heightPixels;
}
imageViewSize.width = width;
imageViewSize.height = height;
return imageViewSize;
}
private static class ImageBeanHolder {
Bitmap bitmap;
ImageView imageView;
String path;
}
private static class ImageViewSize {
int width;
int height;
}
}