Sincronizando dados do Backend

Pedro Salomao
Android Dev BR
Published in
3 min readOct 8, 2015

Esse texto é uma adaptação para Android do algoritmo descrito em: http://matthewmorey.com/core-data-performance-is-a-balance/

Todas as classes retratadas abaixo, juntamente com um exemplo de uso estão disponíveis em: https://github.com/ppgsalomao/FindOrCreateSample

Frequentemente precisamos sincronizar o banco de dados do nosso app com os dados recebidos de um servidor externo, e em alguns casos não podemos optar pela estratégia de apagar tudo e inserir de novo. E aí, como devemos fazer para avaliar um a um recebido contra a base de dados de uma forma um pouco mais eficiente?

O algoritmo, proposto pelo artigo do Matthew Morey que menciono no início do texto, se baseia em um guia de desenvolvimento da Apple (fora do ar no momento em que estou escrevendo esse post), e se chama "Find-or-create" (encontre-ou-crie em tradução literal), e é bem intuitivo e simples. O algoritmo original não pressupõe a remoção de dados que não vem na nova lista de objetos. Por isso fiz algumas adaptações para que ele funcionasse com essa premissa inclusive.

A execução deve se dar da seguinte forma:

  1. Marque todos os dados do banco como não atualizados;
  2. Separe os dados baixados a serem sincronizados em pequenos grupos (batches);
  3. Para cada grupo de dados, carregue na memória os dados do banco que correspondem aos objetos no grupo selecionado (batch);
  4. Ordene as duas listas seguindo um critério comum (um identificador como base);
  5. Itere sobre as duas listas, comparando os elementos um a um, tomando a ação necessária: atualiza se estiver nas duas listas, insere se estiver só na lista de novos objetos e remove se estiver só na lista de objetos antigos.
  6. Ao final, remova todos aqueles que não foram atualizados do banco.

O passo 5 é ilustrado por essa imagem, retirada do post do Matthew:

Os passos 1, 2, 3 e 6 do algoritmo estão intimamente ligados com a persistência adotada pelo desenvolvedor, e por isso vão variar em cada solução.

Já os passos 4 e 5 funcionam da mesma forma independente da base de dados utilizada. Para esses passos criei uma interface com sua respectiva implementação, chamadas: DatabaseSyncStrategy e FindOrCreateDatabaseSyncStrategy.

DatabaseSyncStrategy (interface de uso)

https://github.com/ppgsalomao/FindOrCreateSample/blob/master/app/src/main/java/com/ppgsalomao/findorcreatesample/persistence/strategy/DatabaseSyncStrategy.java

  • setNewObjectsList é usado para definir a lista de objetos recebidos e que devem ser sincronizados com a base;
  • setPersistedObjectsList é usado para definir os objetos que já estão na base de dados para esse grupo de objetos recebidos;
  • updateDatabase faz a mágica, executa os passos 4 e 5 do algoritmo, chamando o delegate a cada passo para realizar a ação específica do banco de dados escolhido.
  • NumericalIdExtractor vem para permitir que você use uma propriedade ou um conjunto de propriedades para definir um identificador sem precisar sobre-escrever uma interface na sua classe de modelo (que pode ser complicado em frameworks como o Realm).
  • DatabaseSyncDelegate será a classe responsável para executar as ações na base de dados, desacoplando o framework de persistência desse algoritmo.

É importante ter cuidado com o reaproveitamento e vazamento de memória nesse processo. Um dos pontos mais importantes de separar em grupos para fazer esse processo é justamente diminuir o "Memory Footprint"(pegada de memória — quantidade de memória utilizada pelo aplicativo).

Para mais informações, bem como a implementação dessa interface, acessem o código fonte do exemplo em: https://github.com/ppgsalomao/FindOrCreateSample

Lembrando que, como tudo em desenvolvimento, nenhuma escolha vem sem alguma consequência. Apesar desse algoritmo funcionar, ele precisa ainda no final remover os intervalos que não foram contemplados pelos grupos, que em algumas bases pode ser bem custoso. Além de precisar de mais um campo nos modelos para representar a atualização. Quando se tem controle da fonte externa de dados, formatos com atualização incremental pode ser mais eficiente para o App, mas acrescenta complexidade à fonte externa de dados.

--

--