我有以下代码来加载图片。我们在GridView上加载了大量图像,并且我们继续耗尽内存。我还能做些什么来减少内存使用量?在代码中我可以调用位图回收?
感谢。
public class ImageManager {
private static ImageManager instance;
final String TAG = "ImageManager";
// Cache: memory and file system
// TODO: HashMap should be replaced with SoftReference or
/// BitmapOptions.inPurgeable(since 1.6)
private File cacheDir;
private HashMap<String, SoftReference<Bitmap>> cache;
// List of images being downloaded right now. To avoid pulling images twice.
private ArrayList<String> downloading;
public ImageManager(){
cacheDir = MyApp.getContext().getCacheDir();
cache = new HashMap<String, SoftReference<Bitmap>>();
downloading = new ArrayList<String>();
}
public static ImageManager getInstance() {
if (instance == null)
instance = new ImageManager();
return instance;
}
public void downloadImage(String url){
downloadAndDisplay(url, null);
}
public void displayImage(String url, ImageView v){
downloadAndDisplay(url, v);
}
//-- Private Methods -------------------------------------------------------
private void downloadAndDisplay(final String url, final ImageView v) {
// Check if image in memory cache. We check disk cache later (it's slow)
Bitmap cachedBitmap = getFromMemoryCache(url);
if (cachedBitmap != null){
AppLog.i(TAG, "Cache Hit (memory), Yay: " + url);
applyImage(url, cachedBitmap, v);
return;
}
// Set view image to null so a reusable view doesn't show it's
// old image while we're waiting for the new one to download.
v.setImageBitmap(null);
// Check if image is already downloading.
if (downloading.contains(url)){
// Already being downloaded. Do nothing.
// TODO: The view might be different from the one we got before,
// we need to consider that possibility.
return;
}
// Download image.
Handler handler = new Handler() {
public void handleMessage(Message message) {
switch (message.what) {
case HttpConnection.DID_SUCCEED: {
AppLog.i(TAG, "Downloaded: " + url);
downloading.remove(url);
// Apply the image to view (on UI thread)
final Bitmap response = (Bitmap) message.obj;
applyImage(url, response, v);
break;
}
case HttpConnection.DID_ERROR: {
downloading.remove(url);
cacheImage(url, null);
//Exception e = (Exception) message.obj;
//e.printStackTrace();
break;
}
case HttpConnection.DID_SKIP: {
applyImage(url, (Bitmap)message.obj, v);
}
}
}
};
HttpRunnable runnable = new HttpRunnable(){
@Override
public Object preFetch(HttpConnection httpConnection) {
// Check if image in cache.
Bitmap cachedBitmap = getFromCache(url);
if (cachedBitmap != null){
AppLog.i(TAG, "Cache Hit, Yay: " + url);
downloading.remove(url);
httpConnection.shouldFetch = false; // no need to fetch
// Cache to memory, we reached this point because it wan't in memory.
cacheImageToMemory(url, cachedBitmap);
return cachedBitmap;
}
else
return true;
}
@Override
public void postFetch(Bitmap bitmap) {
cacheImage(url, bitmap);
}
};
downloading.add(url);
new HttpConnection(handler, runnable).bitmap(url);
}
private void applyImage(String url, Bitmap bitmap, ImageView v){
// Update view if any.
if (v != null){
// Check if the view still intended to display
// the image we have.
if (v.getTag().equals(url)){
AppLog.i(TAG, "--Updating View: " + url);
v.setImageBitmap(bitmap);
}
else {
AppLog.i(TAG, "&&& ImageView recycled &&&: " + url);
}
}
}
private void cacheImageToMemory(String url, Bitmap bitmap) {
cache.put(url, new SoftReference<Bitmap>(bitmap));
}
private void cacheImage(String url, Bitmap bitmap) {
// Cache to memory. Even nulls are cached, they indicate an error in
// loading the image. This way we don't keep trying to download a
// broken image.
cacheImageToMemory(url, bitmap);
if (bitmap == null) {
return;
}
// todo: consider putting date in file name for easy cleaning
//Date date = new Date(0);
//SimpleDateFormat sdf = new SimpleDateFormat ("yyyyMMddHHmmss");
//filename = sdf.format(date);
// Cache to disk
String fileName = String.valueOf(url.hashCode());
File f = new File(cacheDir, fileName);
OutputStream os;
try {
os = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 90, os);
os.close();
} catch (FileNotFoundException e) {
// Ignore. We're creating the file.
} catch (IOException e) {
e.printStackTrace();
}
}
// Fetches image from memory cache, or returns null.
// Doesn't check disk cache. This function is used because it's fast.
private Bitmap getFromMemoryCache(String url){
// Check memory cache
if(cache.containsKey(url))
return cache.get(url).get(); // might return null if soft reference GCed
else {
return null;
}
}
// Fetches image from memory or file cache, or returns null.
private Bitmap getFromCache(String url){
// Check memory cache
Bitmap b = getFromMemoryCache(url);
if(b != null)
return b;
else {
// Check file.
String fileName = String.valueOf(url.hashCode());
AppLog.i(TAG, "Search file cache for: " + fileName);
Bitmap bitmap = BitmapFactory.decodeFile(cacheDir + File.separator + fileName);
return bitmap;
}
}
}
答案 0 :(得分:1)
我建议您使用Android Image Manager
http://code.google.com/p/android-image-manager/
它快速,轻便,高效,高效且易于使用。在大多数情况下,您只需要在代码或布局中用 ManagedImageView 替换android ImageView 。
它有一些反向选项,允许您的应用程序在图像质量,内存使用和性能之间取得平衡。例如,明智的组合将同时加载超过50MB的图像并比标准 ImageView
更快地绘制它们项目维基上的所有功能列表
答案 1 :(得分:0)
我没有看到http响应中图像的解码位置,但您可能会尝试使用BitmapFactory.Options
中的inSampleSize来生成缩略图而不是完整图像。
当您将图像存储到缓存中时,可能会发生这种情况。如果您有一个需要完整图像的活动,您可以从文件缓存中重新加载它的全部荣耀。