Кэширование изображений - растровое изображение не собирается после загрузки другого растрового изображения или закрытия операции 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 МБ +.
Вывод: не доверяйте ГХ и информации об использовании памяти в режиме отладки, особенно если у вас много работающих краткосрочных потоков.