Turbinando o carregamento de imagens remotas no Android
Recentemente, precisei fazer um fine tuning de carregamento de imagens num app.
O cenário era o seguinte: O app consiste em uma série de cardviews com imagens de background oriundos de diversos locais da internet, resultando em alguns problemas:
- Resolver DNS para cada domínio
- Handshake SSL para cada domínio
- Algumas imagens são infinitamente maiores do que o necessário
- Formatos diferentes (JPEG, PNG, GIF, WEBP, etc)
- Latência alta ou mesmo instabilidade
- O fato das imagens serem bem grandes, que imposibilita o OkHttp de fazer cache
- Alto uso de memória
- Não é legal fazer Inline linking
Somando todos esses fatores, a experiência do usuário é prejudicada pela baixa performance.
Resolvi então usar o thumbor como proxy. O thumbor é um projeto open-source mantido pela Globo, que permite aplicar diversos efeitos, cortes, ajustes e filtros em imagens, tudo configurável pela URL.
Exemplo http://thumbor.thumborize.me/unsafe/300x100/filters:rotate(90)/placehold.it/250x150/ff0000
Existe um recurso que usa visão computacional para identificar rostos numa foto e no momento de recortar não cortar nenhum pescoço e/ou enquadrar todas as pessoas.
Com o thumbor eu posso redimensionar a imagem para o tamanho exato que cada dispositivo vai precisar, converter para outro formato menor, remover todas as informações ICC embutidas no arquivo, além de outros filtros disponíveis.
Deploy do thumbor
O deploy do thumbor é bem simples; existem imagens do Docker prontas para serem usadas, buildpacks do Heroku, Elastic Beanstalk ou mesmo manual. Optei por usar o OpsWorks com o Chef, na verdade já existia até um cookbook do thumbor pronto para ser usado.
Content Delivery Network (CDN)
Aproveitei e criei uma CDN usando o CloudFront, assim as respostas do thumbor seriam cacheadas e distribuídas por todo os “points of presence (PoPs)”.
Pollexor
No Android temos uma biblioteca chamada pollexor, que permite escrever a URI do thumbor de uma maneira elegante usando fluent interface.
Como já fazia uso do picasso para o carregamento de imagens, usei o picasso-pollexor, que funciona como um RequestTransformer
para o Picasso, adicionando alguns parâmetros extras na URL de modo que a imagem seja recortada e enquadrada no tamanho exato do ImageView.
Thumbor thumbor = Thumbor.create(BuildConfig.THUMBOR_URL, BuildConfig.THUMBOR_KEY);
final RequestTransformer transformer =
new PollexorRequestTransformer(thumbor);
Picasso picasso = new Picasso.Builder(context)
.requestTransformer(transformer)
.downloader(new OkHttp3Downloader(httpClient))
.build();
Desta forma, o uso do Picasso em conjunto com o thumbor fica totalmente transparente para o programador. Ao informar o tamanho da imagem e o tipo de crop, o PollexorRequestTransformer
entra em ação alterando os parâmetros da URL, de forma totalmente transparente. Prático, não? :)
@Bind(R.id.background)
ImageView background;
...
picasso.load(url)
.fit()
.centerCrop()
.into(background);
Com isso, resolvemos uma série de problemas:
- Será executado apenas um handshake de SSL
- Apenas um domínio a ser consultado no DNS
- A imagem é servida no tamanho exato que será exibida
- A latência é muito menor devido a CDN
- Possibilidade cachear em disco ou memória devido ao tamanho reduzido da imagem
Resultado
Normal | Otimizado |
---|---|
Já acabou, Jéssica?
O próximo passo é descobrir o sentido em que o usuário está rolando a lista e ir fazendo pre-fetch de algumas imagens, passando a impressão de carregamento instantâneo.