public class ImageLoader {
public static final int UNCONSTRAINED = -1;
private static final String TAG = "ImageLoader";
private static final boolean DEBUG = false;
// Queue of work to do in the worker thread. The work is done in order.
private final ArrayList<WorkItem> mQueue = new ArrayList<WorkItem>();
// the worker thread and a done flag so we know when to exit
private boolean mDone;
private Thread mDecodeThread;
private ContentResolver mCr;
private LruCache<String, Bitmap> memoryCache;
private static Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
Map<String, Object> data = (Map<String, Object>) msg.obj;
WorkItem workItem = (WorkItem) data.get("workItem");
Bitmap bitmap = (Bitmap) data.get("bitmap");
workItem.mOnLoadedRunnable.onLoadingComplete(bitmap);
if (DEBUG) Log.i(TAG, String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb + " called listener");
return false;
}
});
public void getBitmap(ImageData image,
OnLoaderCompleteListener imageLoadedRunnable,
boolean isThumb) {
if (mDecodeThread == null) {
start();
}
synchronized (mQueue) {
WorkItem w = new WorkItem(image, imageLoadedRunnable, isThumb);
mQueue.add(w);
mQueue.notifyAll();
}
}
/**
* @param filePath 文件路径
* @return 文件数据
*/
public Bitmap getBitmapFromPath(int minSideLength, int maxNumOfPixels, String filePath) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
if (options.mCancel || options.outWidth == -1
|| options.outHeight == -1) {
return null;
}
options.inSampleSize = computeSampleSize(
options, minSideLength, maxNumOfPixels);
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeFile(filePath, options);
} catch (OutOfMemoryError ex) {
Log.e(TAG, "Got oom exception ", ex);
return null;
}
}
public Bitmap makeBitmap(int minSideLength, int maxNumOfPixels,
Uri uri, ContentResolver cr, ParcelFileDescriptor pfd,
BitmapFactory.Options options) {
try {
if (pfd == null) pfd = makeInputStream(uri, cr);
if (pfd == null) return null;
if (options == null) options = new BitmapFactory.Options();
FileDescriptor fd = pfd.getFileDescriptor();
options.inJustDecodeBounds = true;
// BitmapManager.instance().decodeFileDescriptor(fd, options);
BitmapFactory.decodeFileDescriptor(fd, null, options);
if (options.mCancel || options.outWidth == -1
|| options.outHeight == -1) {
return null;
}
options.inSampleSize = computeSampleSize(
options, minSideLength, maxNumOfPixels);
options.inJustDecodeBounds = false;
options.inDither = false;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
return BitmapFactory.decodeFileDescriptor(fd, null, options);
} catch (OutOfMemoryError ex) {
Log.e(TAG, "Got oom exception ", ex);
return null;
} finally {
closeSilently(pfd);
}
}
public static int computeSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
int initialSize = computeInitialSampleSize(options, minSideLength,
maxNumOfPixels);
int roundedSize;
if (initialSize <= 8) {
roundedSize = 1;
while (roundedSize < initialSize) {
roundedSize <<= 1;
}
} else {
roundedSize = (initialSize + 7) / 8 * 8;
}
return roundedSize;
}
private static int computeInitialSampleSize(BitmapFactory.Options options,
int minSideLength, int maxNumOfPixels) {
double w = options.outWidth;
double h = options.outHeight;
int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 :
(int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));
int upperBound = (minSideLength == UNCONSTRAINED) ? 128 :
(int) Math.min(Math.floor(w / minSideLength),
Math.floor(h / minSideLength));
if (upperBound < lowerBound) {
// return the larger one when there is no overlapping zone.
return lowerBound;
}
if ((maxNumOfPixels == UNCONSTRAINED) &&
(minSideLength == UNCONSTRAINED)) {
return 1;
} else if (minSideLength == UNCONSTRAINED) {
return lowerBound;
} else {
return upperBound;
}
}
private ParcelFileDescriptor makeInputStream(
Uri uri, ContentResolver cr) {
try {
return cr.openFileDescriptor(uri, "r");
} catch (IOException ex) {
return null;
}
}
public void closeSilently(ParcelFileDescriptor c) {
if (c == null) return;
try {
c.close();
} catch (Throwable t) {
// do nothing
}
}
public boolean cancel(final ImageData image) {
synchronized (mQueue) {
int index = findItem(image);
if (index >= 0) {
mQueue.remove(index);
return true;
} else {
return false;
}
}
}
// The caller should hold mQueue lock.
private int findItem(ImageData image) {
for (int i = 0; i < mQueue.size(); i++) {
if (mQueue.get(i).mImage == image) {
return i;
}
}
return -1;
}
// Clear the queue. Returns an array of tags that were in the queue.
public void clearQueue() {
synchronized (mQueue) {
for (WorkItem workItem : mQueue) {
workItem.mImage = null;
workItem.mOnLoadedRunnable = null;
}
mQueue.clear();
}
}
private static class WorkItem {
ImageData mImage;
OnLoaderCompleteListener mOnLoadedRunnable;
boolean mIsThumb;
WorkItem(ImageData image, OnLoaderCompleteListener onLoadedRunnable, boolean isThumb) {
mImage = image;
mOnLoadedRunnable = onLoadedRunnable;
mIsThumb = isThumb;
}
}
public ImageLoader(ContentResolver cr) {
mCr = cr;
//获取系统分配给每个应用程序的最大内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int mCacheSize = maxMemory / 2;
//给LruCache分配1/2最大内存
memoryCache = new LruCache<String, Bitmap>(mCacheSize) {
//必须重写此方法,来测量Bitmap的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}
/**
* 当bitmap被清出缓存的时候,回收所占内存
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
if (key.contains("false")) {
oldValue.recycle();
oldValue = null;
}
if (DEBUG) Log.i(TAG, key + " is removed from cache");
}
};
start();
}
private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null && bitmap != null) {
memoryCache.put(key, bitmap);
}
}
private Bitmap getBitmapFromMemCache(String key) {
return memoryCache.get(key);
}
private class WorkerThread implements Runnable {
// Pick off items on the queue, one by one, and compute their bitmap.
// Place the resulting bitmap in the cache, then call back by executing
// the given runnable so things can get updated appropriately.
public void run() {
while (true) {
WorkItem workItem = null;
synchronized (mQueue) {
if (mDone) {
break;
}
if (!mQueue.isEmpty()) {
workItem = mQueue.remove(0);
} else {
try {
mQueue.wait();
} catch (InterruptedException ex) {
// ignore the exception
}
continue;
}
}
Message msg = new Message();
Map<String, Object> msgMap = new HashMap<>();
msgMap.put("workItem", workItem);
Bitmap bitmap = getBitmapFromMemCache(String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb);
if (bitmap == null) {
if (workItem.mImage.getType() == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
if (workItem.mIsThumb) {
long longId = workItem.mImage.getId();
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmap = MediaStore.Video.Thumbnails.getThumbnail(mCr, longId,
MediaStore.Images.Thumbnails.MINI_KIND, bitmapOptions);
} else {
try {
MediaMetadataRetriever media = new MediaMetadataRetriever();
media.setDataSource(workItem.mImage.getPath());
bitmap = media.getFrameAtTime();
} catch (Exception e) {
Log.e(TAG, "get video bitmap error.");
e.printStackTrace();
bitmap = null;
}
}
} else if (workItem.mImage.getType() == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) {
if (workItem.mIsThumb) {
long longId = workItem.mImage.getId();
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmap = MediaStore.Images.Thumbnails.getThumbnail(mCr, longId,
MediaStore.Images.Thumbnails.MINI_KIND, bitmapOptions);
} else {
bitmap = makeBitmap(UNCONSTRAINED, 3 * 1024 * 1024, workItem.mImage.getUri(), mCr, null, null);
}
}
if (bitmap != null) {
addBitmapToMemoryCache(String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb, bitmap);
if (DEBUG) Log.i(TAG, String.valueOf(workItem.mImage.getId()) + workItem.mIsThumb + " is put in cache");
}
}
msgMap.put("bitmap", bitmap);
msg.obj = msgMap;
handler.sendMessage(msg);
}
}
}
private void start() {
if (mDecodeThread != null) {
return;
}
mDone = false;
Thread t = new Thread(new WorkerThread());
t.setName("image-loader");
mDecodeThread = t;
t.start();
}
public void stop() {
synchronized (mQueue) {
mDone = true;
mQueue.notifyAll();
}
if (mDecodeThread != null) {
try {
Thread t = mDecodeThread;
MediaStore.Images.Thumbnails.cancelThumbnailRequest(mCr, -1, t.getId());
MediaStore.Video.Thumbnails.cancelThumbnailRequest(mCr, -1, t.getId());
t.join();
mDecodeThread = null;
} catch (InterruptedException ex) {
// so now what?
}
}
}
public interface OnLoaderCompleteListener {
void onLoadingComplete(Bitmap bitmap);
}
}
ImageLoader
最新推荐文章于 2021-03-05 11:09:04 发布