КОТ++
Прикладное программирование
Системное программирование
Программирование микроконтроллеров

Если вы дадите человеку программу, то займете его на один день. Если вы научите человека программировать, то займете его на всю жизнь. (с) Васим Латиф

Поиск по тегам

Опубликовано
Комментарии 1

Используете много картинок в своём андроид приложении? И при этом бывает вылетает ошибка OutOfMemory связанная с показом картинок? В таком случае этот пост для вас.

Сам недавно столкнулся с такой проблемой, поэтому в этой статье могу предложить некоторое решение. В моём приложении картинки загружались с сервера и отображались сразу в большом количестве на экране смартфона. И когда хип переполнялся из-за большого количества картинок в памяти приложение падало, падение можно обойти, но картинки всё равно уже не отображаются там где надо. Самое очевидное решение это подгружать только те картинки, которые пользователь может просматривать в данный момент. Но определить какие из ImageView сейчас находятся в видимости пользователя, а какие нет тоже не простая задача. А ведь охото сделать простое и быстрое решение.

На stackoverflow.com предлагали ужимать картинки, а потом выводить на экран. В принципе неплохое решение, но чтоб отобразить сразу много картинок нужно применить 7/10 шакалов ко всем картинкам.

Также видел совет по увеличению хипа для приложения, прописать в манифесте:

android:largeHeap=«true»

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

Остаётся заюзать какую-нибудь библиотеку для подгрузки изображений из инета. На слуху у меня две таких либы: Picasso и Android-Universal-Image-Loader. Мне по некоторым причинам больше приглянулась вторая либа. Её то и попробуем использовать. Подключим в свой проект и пропишем код инициализации. Мне наиболее оптимальным кажется такой вариант (некоторые параметры были найден уже не вспомню на каком форуме):

        Executor downloadExecutor = Executors.newFixedThreadPool(5);
        Executor cachedExecutor = Executors.newSingleThreadExecutor();

ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); int memClass = am.getMemoryClass(); final int memoryCacheSize = 1024 * 1024 * memClass / 8; options = new DisplayImageOptions.Builder() .showImageOnLoading(R.drawable.ic_stub_image) .bitmapConfig(Bitmap.Config.RGB_565) .imageScaleType(ImageScaleType.IN_SAMPLE_INT) .cacheInMemory(true) .cacheOnDisc(true) .build(); File cacheDir = StorageUtils.getCacheDirectory(this); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) .taskExecutor(downloadExecutor) .taskExecutorForCachedImages(cachedExecutor) .memoryCache(new UsingFreqLimitedMemoryCache(memoryCacheSize)) // 2 Mb .diskCache(new LimitedAgeDiskCache(cacheDir, 52428800)) .imageDownloader(new BaseImageDownloader(this, 5 * 1000, 30 * 1000)) .defaultDisplayImageOptions(options) .build(); ImageLoader.getInstance().init(config);

Далее скачиваем и показываем картинку:

ImageLoader.getInstance().displayImage(imageUrl, imageView, MainActivity.options);

Но позже выясняется, что такой вариант особо не спасает. В логах у нас также будет OutOfMemory. А что если грузить сначала превьюшки для изображений (т.е. сжатые маленькие картинки), а если юзер просматривает картинку полностью, то в этот момент подгружать уже оригинал? Попробуем (код для подгрузки превьюхи):

ImageLoader.getInstance().displayImage(imageUrl, imageView, new ImageSize(80, 80));

Для подгрузки оригиналов воспользуйтесь кодом чуть выше. Проверяем, работает!

Данное решение не оптимизирует работу приложения так, как хотелось бы одержимому перфекционисту, но вполне побеждает OutOfMemory!

Теги , ,
Автор

Опубликовано
Комментарии 0

Разрабатывая одно приложение наткнулся на ошибку в переводе времени из одного часового пояса в другой.
Требовалось написать функцию перевода из текущей тайм зоны в тайм зону Москвы.

Получилась такая функция:

public Date getMskDateTime(Date d)
{ SimpleDateFormat dateFormatGmt = new SimpleDateFormat(«yyyy-MMM-dd HH:mm:ss»); dateFormatGmt.setTimeZone(TimeZone.getTimeZone(«GMT+3»));

SimpleDateFormat dateFormatLocal = new SimpleDateFormat(«yyyy-MMM-dd HH:mm:ss»); TimeZone tz = TimeZone.getDefault(); dateFormatLocal.setTimeZone(tz); try { return dateFormatLocal.parse(dateFormatGmt.format(d)); } catch (ParseException e) { return new Date(); } }

После были замечены баги, время переводится не всегда верно.
Проверяю у себя (моё локальное GMT+5) всё отлично работает, у другого же человека, у которого локальное GMT+6, время переводится неверно.
Перевожу у себя на GMT+6 и выявляю ту же ошибку. Перевожу на GMT+7 и опять всё верно переводит.
Т.е. при переводе на GMT+3: GMT+5 переводит на 2 часа, GMT+6 переводит опять на 2 часа (должен на 3), GMT+7 переводит на 4 часа как и должен.
Чудеса!
Ставлю у себя GMT+6. Вывожу то, что хранится в TimeZone.getDefault(). Вижу GMT+6.
Принудительно ставлю GMT+6 вместо default:

TimeZone.getTimeZone(«GMT+6»);

И всё снова верно работает. МАГИЯ!
Думаю, что это баг андроида. Тестил на своём 4.4.

Решил прикрутить небольшой костыль, чтобы избавиться от проблемы.
В итоге решение выглядит так:

public Date getMskDateTime(Date d)
{ SimpleDateFormat dateFormatGmt = new SimpleDateFormat(«yyyy-MMM-dd HH:mm:ss»); dateFormatGmt.setTimeZone(TimeZone.getTimeZone(«GMT+3»));

SimpleDateFormat dateFormatLocal = new SimpleDateFormat(«yyyy-MMM-dd HH:mm:ss»); TimeZone tz = TimeZone.getDefault(); long hours = TimeUnit.MILLISECONDS.toHours(tz.getRawOffset()); long minutes = TimeUnit.MILLISECONDS.toMinutes(tz.getRawOffset()) – TimeUnit.HOURS.toMinutes(hours); minutes = Math.abs(minutes); tz = TimeZone.getTimeZone(String.format(«GMT+%d:%02d», hours, minutes)); dateFormatLocal.setTimeZone(tz); try { return dateFormatLocal.parse(dateFormatGmt.format(d)); } catch (ParseException e) { return new Date(); } }

Теги , , ,
Автор