Android:如何改进此图像管理器?

时间:2011-04-05 21:49:37

标签: android

我有以下代码来加载图片。我们在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;
    }
}

}

2 个答案:

答案 0 :(得分:1)

我建议您使用Android Image Manager

http://code.google.com/p/android-image-manager/

它快速,轻便,高效,高效且易于使用。在大多数情况下,您只需要在代码或布局中用 ManagedImageView 替换android ImageView

它有一些反向选项,允许您的应用程序在图像质量,内存使用和性能之间取得平衡。例如,明智的组合将同时加载超过50MB的图像并比标准 ImageView

更快地绘制它们

项目维基上的所有功能列表

答案 1 :(得分:0)

我没有看到http响应中图像的解码位置,但您可能会尝试使用BitmapFactory.Options中的inSampleSize来生成缩略图而不是完整图像。

当您将图像存储到缓存中时,可能会发生这种情况。如果您有一个需要完整图像的活动,您可以从文件缓存中重新加载它的全部荣耀。