...

понедельник, 7 апреля 2014 г.

[Из песочницы] Экономим память: Picasso vs UniversalImageLoader

Привет, Android-разработчики!

Я думаю, каждый из нас сталкивается с загрузкой изображений по URL. Самый простой способ решения этой задачи: использовать готовую стороннюю библиотеку. Как правило, одним из таких готовых решений оказывается Universal Image Loader (UIL), Picasso. Когда я спрашиваю у разработчика, почему он выбрал ту или иную библиотеку, то, как правило, получаю разные ответы. Например, «у Picasso/UIL нет проблем с memory leaks», или «Square делают только правильные вещи», или просто «Да вот использую UIL, работает – и хорошо».

Так вот, мне стало интересно: какая из этих 2-х библиотек оптимально использует память? Я использую UIL и имею проблему с OutOfMemory на старых устройствах. Возможно, Picasso это лекарство?

Так появилась идея этого benchmark-а.



Цель тестирования: определить, какая из библиотек (UIL или Picasso) минимально использует память устройства.

Тест кейсы:

— Загрузка маленьких изображений (240х240)

— Загрузка больших изображений (>400px по любому из габаритов)

— Загрузка больших изображений и преобразование их размера к габаритам ImageView

— Загрузка маленьких изображений и их показ в виде круглой картинки

— Загрузка больших изображений и показ их в конфигурации RGB565


Методика выполнения теста:

В качестве списка используем GridView шириной в 2 столбца. Адаптер настраивается отдельно под каждый тест кейс. В адаптер отдаем список заранее подготовленных URL, создавая, таким образом, одинаковые условия тестирования.

С периодом в 1 сек, список автоматически делает один проход вниз, а потом вверх с шагом в 4 изображения. По каждому шагу производится измерение памяти, использованной приложением.

Измеряем использованную память в 3 этапа для каждого тест кейса:

— первый запуск — с чистым кешем приложения;

— второй запуск: не закрывая приложение после первого прохода;

— третий запуск – после повторного открытия приложения без чистки кеша.

По окончанию выполнения тест кейса, я дополнительно записывал размер кеша, что тоже немаловажно для старых устройств.


Исходники Benchmark-а можно найти по ссылке

http://ift.tt/1ikFS1D. Проект собран под Gradle.


Итак, ниже результаты по каждому тест кейсу. Ось Y – используемая приложением память в Мб. Ось Х – время проведения тест кейса.


Загрузка маленьких изображений



image

Размер кеша: Picasso=1.39 Мб, UIL=1.17 Мб
Загрузка больших изображений



image

Размер кеша: Picasso=3,67 Мб, UIL=5,44 Мб
Загрузка больших изображений с преобразованием до размера ImageView



image

Размер кеша: Picasso=3,67 Мб, UIL=5,44 Мб
Загрузка маленьких изображений и их обрезка до круглой картинки



image

Размер кеша: Picasso=1.39 Мб, UIL=1.17 Мб
Загрузка больших изображений и показ их в конфигурации RGB565



image

Результаты экспериментов с большими картинками меня впечатлили, и я решил, что стоит попробовать настроить конфигурацию UIL. Чтобы не сильно загружать память кешем – я попробовал отключить у UIL кеш в RAM. И, как вариант, установить кешируемый габарит картинки – не более, чем в половину экрана.


image


На основе эксперимента я сделал следующие выводы:



  • Если ваши списки работают с маленькими изображениями (сравнимыми с размером ImageView) – выбор библиотеки для вас не принципиален. Picasso создает чуть больший кеш на диске, при этом используя меньше RAM примерно на тот же размер.

  • Picasso показала потрясающие результаты по управлению памятью, работая с большими изображениями. UIL, по всей видимости, хранит оригинал изображения в памяти. Picasso хранит уже преобразованный размер картинки. Потому и кеш на диске у Picasso значительно меньше.

  • UIL может работать с той же эффективностью, что и Picasso, если его дополнительно настроить. Например, ограничить размер кеша в памяти. Или, как в одном из тестов – ограничить вручную размер кешируемых фотографий. Второй способ может быть непригоден для использования, поскольку устанавливает глобальную конфигурацию ImageLoader-а.

  • Работа с круглыми аватарами обходится «дешевле» через Picasso. Но, опять же, за счет того, что я вручную вызывал recycle() у оригинального Bitmap-а. Такое же действие можно выполнить и в UIL, устанавливая переопределенный BitmapDisplayer.

  • Picasso предельно проста в использовании и уже «с коробки» работает с памятью эффективно. Так выглядит инициализация и выполнение загрузки для библиотек:

    Picasso


    public class PicassoSquareFitAdapter extends BaseBenchmarkAdapter {

    public PicassoSquareFitAdapter(Context context, IUrlListContainer urlListContainer) {
    super(context, urlListContainer);
    }

    @Override
    protected void loadImage(ImageView imageView, String url) {
    Picasso.with(context).load(url).fit().into(imageView);
    }
    }



    UIL


    public class UILSquareFitAdapter extends BaseBenchmarkAdapter {

    private DisplayImageOptions options;

    public UILSquareFitAdapter(Context context, IUrlListContainer urlListContainer) {
    super(context, urlListContainer);
    ImageLoaderConfiguration config = ImageLoaderConfiguration.createDefault(context);
    ImageLoader.getInstance().init(config);
    options = new DisplayImageOptions.Builder()
    .imageScaleType(ImageScaleType.EXACTLY)
    .resetViewBeforeLoading(true)
    .cacheInMemory(true)
    .cacheOnDisc(true)
    .build();
    }

    @Override
    protected void loadImage(ImageView imageView, String url) {
    ImageLoader.getInstance().displayImage(url, imageView, options);
    }
    }





  • Есть у Picasso и минус: трансформации изображений и приведение к RGB565 необходимо делать в самописных классах.

    RoundTransformation


    public class RoundTransformation implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {
    int size = Math.min(source.getWidth(), source.getHeight());

    int x = (source.getWidth() - size) / 2;
    int y = (source.getHeight() - size) / 2;

    Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
    if (squaredBitmap != source) {
    source.recycle();
    }

    Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());

    Canvas canvas = new Canvas(bitmap);
    Paint paint = new Paint();
    BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
    paint.setShader(shader);
    paint.setAntiAlias(true);

    float radius = size / 2f;
    canvas.drawCircle(radius, radius, radius, paint);

    squaredBitmap.recycle();
    return bitmap;
    }

    @Override
    public String key() {
    return "circle";
    }
    }





    Config565Transformation


    public class Config565Transformation implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {
    Bitmap resultBitmap = Bitmap.createBitmap( source.getWidth(), source.getHeight(), Bitmap.Config.RGB_565 );
    Canvas canvas = new Canvas(resultBitmap);
    Paint paint = new Paint();
    paint.setFilterBitmap(true);
    canvas.drawBitmap(source, 0, 0, paint);
    source.recycle();
    return resultBitmap;
    }

    @Override
    public String key() {
    return "Config565Transformation";
    }
    }







  • Для себя я сделал вывод: проекты необходимо переводить на Picasso. В моем случае это решит проблему с перерасходом памяти. Надеюсь, этот пост будет полезен и Вам!


This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий