/*
* Copyright (C) 2014 The AppCan Open Source Project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.zywx.wbpalmstar.base.cache;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.os.Process;
import org.zywx.wbpalmstar.base.BDebug;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 图片异步加载与缓存管理
*/
public class ImageLoaderManager {
private static final String TAG = "ImageCacheManager";
public static final String MAP_KEY_BITMAP = "bitmap";
public static final String MAP_KEY_TASK = "task";
public static final int ACTION_LOAD_COMPLETED = 1;
public static final int ACTION_LOAD_START = 2;
public static final int ACTION_LOAD_COUNT = 3;
public static final int ACTION_LOAD_FINISH = 4;
public static final int DEFAULT_CONCURRENT_THREAD_SIZE = 3;
public static final int SOFT_REFERENCE_MAX_SIZE = 40;
// LruCache可控回收力度而不内存溢出
public LruCache<String, Bitmap> memoryCache;
private LinkedList<ImageLoadTask> taskList = new LinkedList<ImageLoadTask>();
private ImageLoadStatusListener countListener;
private static ImageLoaderManager loaderManager;
private ExecutorService executorService;
/**
* @param maxConcurrentThreads 最大并发线程数
*/
private ImageLoaderManager(int maxConcurrentThreads, int memoryCacheSize) {
memoryCache = new LruCache<String, Bitmap>(memoryCacheSize) {
/**
* 当调用lruCache.get(String key)方法返回值为null时,会调用此create(String
* key)方法为此key创建一个Value
*/
@Override
protected Bitmap create(String key) {
return super.create(key);
}
// 计算bitmap在内存中占用字节数-->height*width*4(bytes)
/**
* 计算此Value在内存中占用的字节大小, 以此来衡量是否达到了LurCache的最大容量。
* 为在恰当时机释放之前的key-value提供依据
*/
@Override
protected int sizeOf(String key, Bitmap value) {
if (value == null) {
return super.sizeOf(key, value);
} else {
final int bytes = value.getRowBytes() * value.getHeight();
return bytes;
}
}
/**
* 超过LruCache最大容量,移除硬引用,为了回收利用,放入软引用表里面
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
}
};
executorService = Executors.newFixedThreadPool(maxConcurrentThreads);
}
public synchronized static ImageLoaderManager getInstance(int maxConcurrentThreads, int memoryCacheSize) {
if (loaderManager == null) {
loaderManager = new ImageLoaderManager(maxConcurrentThreads, memoryCacheSize);
}
return loaderManager;
}
public static ImageLoaderManager getDefaultInstance() {
return loaderManager;
}
public static ImageLoaderManager initImageLoaderManager(Context context) {
if (context == null) {
throw new NullPointerException("context can't be null!");
}
ImageLoaderManager imageLoaderManager = ImageLoaderManager.getDefaultInstance();
if (imageLoaderManager == null) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
final int memoryClass = activityManager.getMemoryClass();
final int memoryCacheSize = memoryClass / 8 * 1024 * 1024;
imageLoaderManager = ImageLoaderManager.getInstance(3, memoryCacheSize);
}
DiskCache.initDiskCache(context);
return imageLoaderManager;
}
public Bitmap getCacheBitmap(String url) {
if (url == null) {
return null;
}
return memoryCache.get(url);// 查找硬引用
}
public void setOnCountListener(ImageLoadStatusListener listener) {
this.countListener = listener;
}
public void asyncLoad(ImageLoadTask task) {
synchronized (taskList) {
final int findIndex = taskList.indexOf(task);
if (findIndex != -1) {// find task in taskList
final ImageLoadTask item = taskList.get(findIndex);
// in order to prior display newest task result for user,
// move ready task to taskList rear
if (item.getStatus() == ImageLoadTask.STATUS_READY) {
taskList.remove(findIndex);
taskList.addLast(item);
}
} else {// not found,add to taskLisk last
if (taskList.size() == 0) {
handler.sendEmptyMessage(ACTION_LOAD_START);
}
taskList.addLast(task);
executorService.execute(new TaskWorker());
}
}
}
public void addDelayTask(ImageLoadTask task) {
synchronized (taskList) {
int index = taskList.lastIndexOf(task);
if (index == -1) {
taskList.addFirst(task);
executorService.execute(new TaskWorker());
}
}
}
public void clear() {
memoryCache.evictAll();
removeAllTask();
}
public void removeAllTask() {
synchronized (taskList) {
taskList.clear();
}
}
private class TaskWorker implements Runnable {
private ImageLoadTask seekReadyTask() {
synchronized (taskList) {
for (int i = taskList.size() - 1; i >= 0; i--) {
final ImageLoadTask loadTask = taskList.get(i);
if (loadTask.getStatus() == ImageLoadTask.STATUS_READY) {
loadTask.setStatus(ImageLoadTask.STATUS_STARTED);// 改变其任务状态
return loadTask;
}
}
}
return null;
}
private Bitmap loadFromSource(ImageLoadTask loadTask) {
Bitmap bitmap = loadTask.startExecute();
if (bitmap != null) {// 从源取得bitmap成功,将bitmap写入内存文件缓存起来
DiskCache.writeDiskCache(loadTask.getKey(), bitmap);
}
return bitmap;
}
public void run() {
/* 设置任务线程为后台线程,获得更少的执行机会,减少对UI渲染的影响 */
int threadPriority = Process.getThreadPriority(Process.myTid());
if (threadPriority != Process.THREAD_PRIORITY_LOWEST) {
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
}
final ImageLoadTask loadTask = seekReadyTask();
if (loadTask == null) {
handler.sendEmptyMessage(ACTION_LOAD_FINISH);
return;
}
try {
// 从磁盘去取
Bitmap bitmap = DiskCache.readCache(loadTask.getKey());
if (bitmap == null) {// 尚未缓存,从源去取
bitmap = loadFromSource(loadTask);
}
if (bitmap != null) {// 取得图片成功
// 将bitmap放入LruCache缓存列表
memoryCache.put(loadTask.filePath, bitmap);
}
final Bitmap finalBitmap = bitmap;
if (loadTask.getCallBack() != null) {
handler.post(new Runnable() {
@Override
public void run() {
loadTask.performCallback(finalBitmap);
}
});
}
} catch (OutOfMemoryError e) {
memoryCache.evictAll();
System.gc();
e.printStackTrace();
BDebug.e(TAG, "OutOfMemoryError!!!!!!!!!!!!!!!!!!!!:" + e.getMessage());
} finally {
if (loadTask != null) {
synchronized (taskList) {
taskList.remove(loadTask);
}
}
}
}
}
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case ACTION_LOAD_START:
if (countListener != null) {
countListener.onImageLoadStart(ImageLoaderManager.this);
}
break;
case ACTION_LOAD_FINISH:
if (countListener != null) {
countListener.onImageLoadFinish(ImageLoaderManager.this);
}
break;
}
}
};
public static interface ImageLoadStatusListener {
void onImageLoadStart(ImageLoaderManager manager);
void onImageLoadFinish(ImageLoaderManager manager);
}
}