1. A biblioteca Android-State permite reter o estado de objetos em atividades, fragmentos e visualizações em aplicativos Android de forma simples.
2. Ela funciona internamente utilizando o SaveInstanceState, mas de forma transparente ao desenvolvedor, evitando código boilerplate.
3. Há casos em que o ViewModel é mais apropriado, como quando se precisa reter listas grandes de objetos, devido às limitações do SaveInstanceState.
2. Retenção de objetos em aplicativos Android
A retenção de estados de objetos em aplicativos
Android é algo já trabalhado desde as primeiras
versões desta plataforma.
As estratégias que se destacam são: utilizando o
ViewModel ou utilizando o SaveInstanceState. Mas
também é possível trabalhar bases de dados locais
para isso, como, por exemplo: SharedPreferences,
SQLite ou Realm.
Não há uma solução superior a todas as outras, há a
que se encaixa melhor para um problema em
específico. O importante é permitir que objetos
trabalhados em atividades e fragmentos, ao menos
esses, não sejam criados novamente devido a
reconstrução de algum destes componentes Android.
A reconstrução de objetos não deve ser uma
preocupação somente caso não tenha o mínimo
efeito na performance do aplicativo.
4. Histórico
O time de desenvolvimento do Evernote aprimorou
a API Icepick, API já famosa por conseguir, de
maneira trivial, salvar o estado de objetos de
atividades, fragmentos e visualizações. Com isso
surgiu a Android-State library.
A Android-State é mais robusta, funciona tanto para
projetos Java como para projetos Kotlin. Além de
não exigir que as propriedades sejam públicas, algo
que uma API não deve fazer: forçar
desenvolvedores a seguir o caminho dela de
arquitetura de software.
Como o Icepick, o Android-State também é de
código aberto. Importante ter em mente que o que o
Android-State faz é exatamente o que o Icepick faz,
mas abrangendo mais ramos, atacando as
limitações da Icepick API.
5. Ok, mas o que realmente essas APIs fazem?
Elas trabalham o SaveInstanceState para nós developers, evitando que tenhamos
de adicionar código boilerplate ao projeto.
Então ainda temos presente nestas bibliotecas as limitações do
SaveInstanceState?
Sim. Veja a tabela a seguir:
Android-State / Icepick ViewModel
Durabilidade dos dados em memória Maior Menor
Quantidade de dados (bytes) em memória Menor Maior
Auxílio de Interface para serialização e
desserialização de objetos
Sim Não
Lembrando que a tabela acima somente lhe ajuda a escolher qual a melhor API
para o seu domínio do problema.
6. Instalação da API
No Gradle App Level, ou build.gradle (Module: app), de seu projeto Android,
adicione as seguintes referências:
Logo depois sincronize o projeto. Pode ser que você tenha problemas de conflito
de API caso não esteja com a versão mais atual da Android Support APIs, logo,
caso ocorra, utilize como estratégia de correção de conflito: atualização da
Android Support APIs para a versão mais atual.
Na época da construção deste conteúdo a versão mais atual era a 27.1.0.
7. Utilizando via Application class
Para que seja possível manter o estado de instâncias em atividades e fragmentos
de todo o projeto, primeiro temos de criar uma classe Application customizada
com o StateSaver definido:
Depois devemos referencia-la no AndroidManifest.xml:
8. E assim utilizar a anotação @State nas entidades que queremos manter o estado:
Diferente da Icepick API, com Android-State as propriedades podem ser públicas
e privadas. No código acima a inicialização das propriedades não mais será
levada em conta caso haja uma reconstrução de atividade, somente os algoritmos
dentro, direta ou indiretamente, dos métodos de ciclo de vida da atividade.
A anotação funciona exatamente com a mesma sintaxe quando com fragmentos:
9. Importante: somente tipos de dados aceitos em objetos Bundle é que são
válidos de uso com o @State. São eles: String; todos os tipos primitivos; e
objetos de tipos que implementam Parcelable ou Serializable.
10. Utilizando somente em componentes necessários
Quando a necessidade de retenção de estado é somente em alguns componentes
do projeto, você pode ignorar a definição utilizando uma classe Application
customizada e assim utilizar os métodos restoreInstanceState() e
saveInstanceState() do StateSaver:
Note que o outState!! tem
as duas exclamações ao
final para que o code
inspector não acuse erro e
assim trave a compilação.
Isso, pois
StateSaver.saveInstanceS
tate() está esperando um
dado não null, porém para
não modificar a assinatura
nativa de
onSaveInstanceState()
optei por gerar uma
exceção caso o outState
seja nulo.
12. Utilizando com visualização
Para manter o estado de propriedades em visualizações é preciso o trabalho
explícito com a classe StateSaver na View, como a seguir:
Seguindo as indicações da documentação fica entendido que a definição global
de uso do Android-State, com uma classe Application, não funciona para estados
de propriedades em visualizações customizadas.
Visualizações customizadas? Sim, pois por padrão, ao menos para as Views de
entrada de dados, o sistema Android já mantém em tela o que foi informado pelo
usuário, isso caso haja reconstrução de atividade ou fragmento.
13. Envelopando objetos não aceitos em Bundle
Caso você entre em um domínio de problema onde será necessário manter o
estado de objetos de tipos que não implementam nem Parcelable e nem
Serializable, para isso você terá de criar uma classe envelopadora que
implemente Bundler<T>.
Esse tipo de situação é comum quando a tipo de objeto é de alguma das APIs de
terceiros vinculadas ao seu projeto. Veja a classe a seguir:
Assuma que os tipos Country e Date implementam ou o Parcelable ou o
Serializable. Mesmo assim para mantermos algum objeto do tipo Game teremos
de criar uma classe envelopadora (assuma que não podemos implementar em
Game as interfaces comentadas anteriormente).
14. Podemos criar a classe GamerBundler como classe envelopadora que
implementa Bundler<T>:
15. Agora, em uma atividade de exemplo, temos:
Note que caso você esteja em um domínio do problema onde seja necessária a
criação de mais de uma classe Bundler<T>, onde uma conterá a outra, para isso
recomendo que utilize o ViewModel como solução para retenção de estados de
objetos dos componentes Activity e Fragment.
Isso, pois você terá de criar uma outra rota para conseguir essa meta, por
exemplo: criar uma classe para conter todos os dados das classes que não
implementem as interfaces Parcelable e Serializable. Sendo assim a
simplicidade adquirida com a Android-State API é perdida favorecendo então a
escolha pelo ViewModel.
16. Caso você precise trabalhar com o tipo List<T>, vai notar que o compilador não
permitirá prosseguir, pois List<T> não tem em sua hierarquia o trabalho com
Parcelable ou Serializable. Para isso a biblioteca Android-State já tem como
nativas as seguintes classes envelopadoras:
- BundlerListParcelable;
- BundlerListCharSequence;
- BundlerListString;
- BundlerListInteger.
Exemplo:
17. Trabalhando com reflexão
Se por algum motivo as propriedades que devem ser mantidas são privadas e
você não pode fornecer os métodos getter e setter delas, terá de utilizar a
anotação @StateReflection para mante-las. Trabalho com reflexão, algo
recomendado pela API somente quando não há outra alternativa:
18. Proguard
Caso esteja com o Proguard ativo, terá de colocar as seguintes definições em
proguard-rules.pro:
19. Pontos negativos
- A API não permite o trabalho com classes Bundler<T> internas a outras classes
que implementem Bundler<T>;
- Para realmente entender a API Android-State é preciso ler também a
documentação da API Icepick;
- Na documentação não fala nada sobre as limitações da API devido ao uso
interno do SaveInstanceState, algo que um developer precisa saber para não
cometer o erro de tentar salvar listas com centenas de objetos pesados em
memória.
20. Pontos positivos
- Muito mais simples de utilizar do que qualquer uma das outras soluções Android
para retenção de estado de objetos de atividades, fragmentos e visualizações;
- Funciona para todas as versões Android ainda em mercado;
- Quando necessária, a criação de classe Bundler<T> ainda mantém o uso da
API sendo simples;
- Tem definições para o Lint Tool que facilitam o uso da API no Android Studio.
21. Conclusão
A Android-State API é simples, isso induz ao seu uso, mas há casos onde é
prudente realizar testes de carga, pois é a API SaveInstanceState que está sendo
utilizada "por debaixo dos panos" e sabemos da limitação de espaço em memória
para está API.
Para projetos onde os dados são pouco ou nada dinâmicos e mesmo assim não
devem passar pelas reconstruções de atividades, fragmentos e visualizações,
seguramente recomendo Android-State. Para outras situações: ficaria com o
ViewModel. Também por esta API fazer parte da arquitetura recomendada pelo
Google Android.
22. Fontes
Conteúdo completo, em texto e em vídeo, no link a seguir:
- https://www.thiengo.com.br/como-reter-objetos-utilizando-android-state-api
Fontes:
- https://github.com/evernote/android-state
- https://github.com/frankiesardo/icepick
23. Para estudo
- Treinamento oficial:
- Prototipagem Profissional de Aplicativos Android.
- Meus livros:
- Receitas Para Desenvolvedores Android;
- Refatorando Para Programas Limpos.
- Redes:
- Udemy;
- YouTube;
- Facebook;
- LinkedIn;
- GitHub;
- Twitter;
- Google Plus.
- Blog App.
24. Como Reter Objetos Utilizando
Android-State API
thiengo.com.br
Vinícius Thiengo
thiengocalopsita@gmail.com