Кэширование изображений - растровое изображение не собирается после загрузки другого растрового изображения или закрытия операции ImageView.

Я пытаюсь реализовать что-то, что очень похоже на UrlImageViewHelper ( https://github.com/koush/UrlImageViewHelper), где вы можете легко с помощью простой одной строки кода, загрузить изображения из URL-адреса, и если изображение уже было загружено вместо этого он загружается из кеша. Основное различие заключается в том, что я хочу получить тот же эффект, но вместо того, чтобы загружать его с URL-адреса, я хочу получать изображения со своего собственного сервера, используя свое собственное соединение клиент-сервер. Каждое изображение на моем сервере может быть уникально идентифицировано строкой, и я использую это как идентификатор для изображения.

Моя основная идея заключалась в следующем: использовать кэш LRU для хранения изображений, но вместо удержания растровых изображений (которые очень большие) я хочу сохранить двоичные данные необработанных изображений, чтобы я мог использовать одно и то же изображение для создания растровых изображений разных размеров и качества по требованию в зависимости от конкретной ситуации.

Это моя реализация до сих пор:

    public class ImageHandler {

    private static class BitmapCache extends LruCache<String, byte[]>
    {
        public WigoBitmapCache(int maxSize) {
            super(maxSize);
        }

        @Override
        protected int sizeOf(String key, byte[] value) {
            return value.length;
        }

    }

    private static class ImageHandlerThread extends Thread
    {

            /* THIS THREAD WILL DECODE THE IMAGE AND SET THE BITMAP TO THE IMAGEVIEW IN THE BACKGROUND */   
                Activity activity;
        ImageView imageView;
        byte[] imageBytes;

        public ImageHandlerThread(Activity activity, ImageView imageView, byte[] imageBytes)
        {
            this.activity=activity;
            this.imageView=imageView;
            this.imageBytes=imageBytes;
        }

        public void run() {

            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o);
            int factor1=o.outHeight/height;
            int factor2=o.outWidth/width;
                        /* height and width are for now constant */
            o = null;
            o = new BitmapFactory.Options();
            if (factor1>factor2)
                o.inSampleSize=factor1;
            else
                o.inSampleSize=factor2;
            Bitmap bit = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length,o);
            setBitmap(bit);
            bit = null;

        }


        private void setBitmap(final Bitmap bit) {
            activity.runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    imageView.setImageBitmap(bit);

                }
            });     
        }

    }

    private static class QueueItem
    { /*USED TO HOLD INFO ABOUT THE IMAGE REQUEST UNTIL THE IMAGE GETS FROM THE SERVER */
        String imageName;
        Activity activity;
        ImageView imageView;

        public QueueItem(String imageName, Activity activity, ImageView imageView) 
        {
            this.imageName=imageName;
            this.activity = activity;
            this.imageView = imageView;
        }

    }

    private BitmapCache cache; // this cache holds the image binaries 
    private ArrayList<QueueItem> queue; // this queue holds the info about the request, until the server sends the image

    public ImageHandler(int maxSize)
    {
        cache=new BitmapCache(maxSize);
        queue = new ArrayList<QueueItem>();

    }

    public synchronized void setBitmap(Activity activity, ImageView imageView, String imageName)
    {
        byte[] imageBytes = cache.get(imageName);
        if (imageBytes==null)
        {
            QueueItem item = new QueueItem(imageName, activity, imageView);
            queue.add(item);    

            /* HERE IS THE CODE TO RETRIEVE THE IMAGE BINARY FROM MY SERVER, THIS CODE WORKS FINE, SO THERE IS NO REASON TO BOHER YOU WITH IT */

        }
        else
        {
            ImageHandlerThread thread = new ImageHandlerThread(activity, imageView, imageBytes);
            thread.start();
        }

    }

    public synchronized void insert (String imageName, byte[] imageBytes)
    {

        /* THIS METHOD IS THE CALLBACK THAT IS CALLED WHEN THE IMAGE BINARY IS RECEIVED FROM THE SERVER */

        cache.put(imageName, imageBytes);

        for (QueueItem item: queue)
        {
            if (item.imageName.equals(imageName))
            {
                ImageHandlerThread thread = new ImageHandlerThread(item.activity, item.imageView, imageBytes);
                thread.start();
                queue.remove(item);
            }
        }
    }
}

По сути, основным методом здесь является setBitmap(), он получает активность, imageView, которому требуется растровое изображение, и имя имени изображения. Если изображение уже находится в кэше, запускается новый поток для декодирования байтов в точечный рисунок правильного размера и установки растрового изображения в imageView. Если изображение отсутствует в кэше, запрос помещается в очередь до тех пор, пока изображение не будет получено, изображение извлекается с сервера, а затем запускается тот же поток, что и прежде.

Все это работает абсолютно нормально, проблема в том, что когда для imageView установлена ​​другая битовая карта для изображения или даже когда активность уничтожена, битовая карта все еще находится в памяти и не собирается GC.

Сначала я подумал, что это потому, что я держал ссылку на действие, и эта ссылка поддерживает активность, но, похоже, это не так, моя ссылка на активность очень коротка, и как только изображение приходит из На сервере эта ссылка очищена.

Мне не хватает памяти быстро с этой реализацией, и я понятия не имею, почему или что делать, чтобы это исправить. Созданные мной растровые изображения не собираются, хотя я не храню ссылки на них. Может ли это быть артефактом способа, которым я декодирую изображения? или темы хранят ссылки, которые не собраны должным образом? У кого-нибудь есть идеи?

1 ответ

Решение

Итак, я неправильно понял проблему, то, что происходило, было то, что GC не собирал законченные объекты потока, потому что я выполнял это в режиме отладки, а не в режиме выполнения. Когда я запускал это в режиме запуска, использование памяти моим приложением вообще не превышало 8 МБ, а когда он находился в режиме отладки, я попал в область 25 МБ +.

Вывод: не доверяйте ГХ и информации об использовании памяти в режиме отладки, особенно если у вас много работающих краткосрочных потоков.

Другие вопросы по тегам