Android: как мне улучшить этот менеджер изображений?

1

У меня есть следующий код для загрузки изображений. Мы загружаем много изображений в 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 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 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
    Вы собираетесь показывать миниатюры или полные изображения в GridView ?
  • 0
    Вместо кеширования объекта изображения, как насчет кеширования сжатых потоков?
Теги:

2 ответа

1

Я рекомендую вам Android Image Manager

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

он быстрый, легкий, эффективный, эффективный и очень простой в использовании. в большинстве случаев все, что вам нужно сделать, будет заменять android ImageView на ManagedImageView в вашем коде или макетах.

у него есть некоторые сложные варианты, которые позволяют вашему приложению балансировать между качеством изображения, использованием памяти и производительностью. Например, мудрая комбинация будет загружать более 50 МБ изображений одновременно и нарисовать их быстрее, чем стандартный ImageView

список всех функций находится в вики проекта

0

Я не вижу, где происходит декодирование изображений из HTTP-ответа, но вы можете попробовать использовать inSampleSize от BitmapFactory.Options для создания уменьшенных изображений вместо полных изображений.

Это, вероятно, должно произойти при хранении изображений в вашем кеше. Когда у вас есть активность, которая требует полного изображения, вы можете перезагрузить ее из кеша файлов в полной мере.

  • 0
    Мэтт, спасибо за ответ. Мы уже отправляем только уменьшенные изображения. Мы могли бы попробовать пересмотреть его, чтобы понизить качество и посмотреть, будет ли это иметь значение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню