Прозрачное кеширование изображений во Flash/Flex

    Вкратце — эта техника ускоряет показ множества одинаковых изображений (с одинаковым url), избавляет от повторных загрузок и экономит память. Решения из google-поиска не впечатлили, т.к. там предлагаются только самые базовые варианты без учёта частностей, к тому же их внедрение в проект не тривиально.
    В данном решении достаточна замена Image на ImageCached для достижения эффекта. В конце поста ссылка на тестовое приложение, наглядно показывающее преимущество кеширования.

    С чего всё начиналось:
    В текущем проекте очень много работы с графикой, и столкнулся со следующей проблемой — каждый экземпляр класса Image с одинаковым URL создаёт под себя загрузчик и грузит себя (пусть даже и из кеша). Особенно хорошо это заметно при организации drag'n'drop'а картинок, когда при перетаскивании создаётся proxy картинки, картинка подгружается не сразу, и в результате заметно моргание.
    Собств-но с этого полусекундного моргания и родился этот пост.

    Кеш изображений, описанный в большинстве примеров, основан на том, что свойству source класса Image может быть присвоен как URL адрес, так и объект класса Bitmap (в общем случае реализатор интерфейса IDisplayObject).

    Идея проста — в случае, если изображение с таким source уже было загружено — подставлять вместо URL bitmap загруженного изображения.
    вариант 1:
    — если в кеше нет изображения с таким url, то начать его загрузку
    — иначе взять из кеша загруженный Bitmap и установить его как source
    при завершении загрузки:
    — установить загруженный bitmap как source
    — поместить загруженный bitmap в кеш
    Этот вариант вообщем был достаточен для проекта, но есть у него частность, которая при текущих мощностях сомнительна, но «не красива».
    Дело в том, что при асинхроннной загрузке, 20 одновременно выводимых картинок с одинаковым (и незакешированным) URL приведут в 1ом варианте к 20 созданиям загрузчика. Посему был создан

    вариант 2:
    — если в кеше нет записи с таким URL, то создать её, поставить объект в очередь ожидания и начать загрузку
    — если в кеше есть запись с таким URL и есть данные, то установить их как source
    — если в кеше есть запись с таким URL и нет даных, поставить объект в очередь ожидания
    загрузчик варианта2:
    — поместить загруженный bitmap в кеш
    — пройтись по ожидающим объектам, установить им загруженный bitmap как source и удалить из списка ожидания

    Этот вариант корректно обрабатывает одновременную загрузку одного URL несколькими объектами, однако и у него есть частность.
    Пример — форма со списком элементов и окно, в котором показывается картинка текущего элемента. При быстром проходе по списку, source у картинки может меняться быстрее чем сможет загрузиться предыдущее изображение что приводит к разсинхронизации кеша.
    Решение собств-но в одну строчку
    вариант3:
    — если объект есть в очереди ожидания, то удалить его из очереди (загрузчик всё равно поместит в кеш предыдущую картинку, но source не будет обновлён устаревшими данными)
    … далее как в варианте2…
    В результате все изображения будут кешироваться, но как source установится только последнее.

    Реализация:
    Класс ImageCached расширяющий класс Image, с переопределением setter'а для свойства source (код ниже)

    Итог:
    В проекте класс Image find-and-replace'ом заменён на ImageCached и о нём забыто.
    По коду сразу скажу — это мой первый проект на flash/as3/flex, аналога std:map для кеша не нашёл, буду рад критике.

    Тестовое приложение — click

    Создаёт два изображения, сразу после этого обменивает им source друг на друга, при движении мышки создаёт под курсором Image/ImageCached со временем жизни в 2 секунды.
    Переключение между Image и ImageCached — по клику (также вызывается garbage collector)
    Выводится график потребления памяти (компонент нагуглен)

    Собств-но хорошо видно как скачет память и общее быстродействие при использовании Image и положительный эффект от ImageCached.

    Исходники — click, ну или можно в приложении правый клик -> View Source

    Комментарии 4

      0
      У вас довольно неплохой компонент, но меня даже больше заинтересовал MemoryMonitor.

      А вообще, проблему с кешированием картинок пробовал решить один довольно известный флексер здесь: www.quietlyscheming.com/blog/2007/01/23/some-thoughts-on-doubt-on-flex-as-the-best-option-orhow-i-made-my-flex-images-stop-dancing/, правда, у него была задача избавиться от мелькания в компонентах типа «List».
        0
        Если честно, то компонент Image вообще какой-то кривоватый.
        При работе с картинками я сделал специальную обвертку вокруг Bitmap, пронаследовав UIComponent, а загрузкой непосредственно графики занимается отдельнй класс, использующий Loader. Загруженная BitmapData затем асайнится моему компоненту. В классе, отвечающем за загрузку, реализован 1й вариант кеширивания, который вам не подошел, но мне такой подошел отлично. Хотя можно и реализовать и вашу логику.

        Спс за сорс!
          0
          Спасибо за комментарии.
          Нашёл что-то вроде бага во flash под FireFox (под opera/ie всё норм) — если есть Image, для которого source'ом является Bitmap, и этот Bitmap обновляется тем же bitmapData, то первым кадром выводится полная картинка (без увеличения/уменьшения) и только на следующем кадре она выводится в заданном размере (flash берёт «паузу»)
          Лечится просто — вставить перед
          super.source = new Bitmap(cached_imageData[cachedFilenameID]);
          проверку
          if((super.source is Bitmap) && (super.source.bitmapData != null) && (super.source.bitmapData == cached_imageData[cachedFilenameID]))
          return;

          Это вообще надо в код добавить, чтобы не создавать лишний Bitmap каждый раз
            0
            Попробуй использовать Dictionary для кеширования и URL как ключ, намного удобнее будет.

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое