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

标签 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;
    }
}

最佳答案

我推荐你 Android Image Manager

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

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

它有一些复杂的选项,可以让您的应用程序在图像质量、内存使用和性能之间取得平衡。例如,明智的组合将一次加载超过 50MB 的图像并比标准 ImageView

更快地绘制它们

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

关于Android:如何改进此图像管理器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5558978/

相关文章:

android - 如何生成一个随机数,然后将其显示在屏幕上?

java - 如何打印从 HttpResponse 返回的消息?

android - 没有 UI 的 Activity

android - 启动 Activity 的小部件

java - 使用 Toast 监控传入的短信

应该只安装在手机上而不是平板电脑上的 Android 应用程序

android - 从特定日期获取日期、月份和年份

Android:ValueAnimator 总是给出最终值

Android TTS 说 0 为 "oh"

android - 将参数传递给 Jetpack Compose 中的嵌套导航图