Expor a magia do DiffUtil


Cada desenvolvedor do Android usou o RecyclerView para exibir as listas e cada um enfrentou o problema de atualizar os dados na lista até que a classe mágica DiffUtil apareceu em 2016. Vou explicar nos dedos como ele realmente funciona e tentar dissipar sua magia.


Um pouco de história


Um dos elementos mais comuns em aplicativos móveis é a lista, no nosso caso, o RecyclerView . Podem ser listas de qualquer coisa: endereços de escritórios, listas de amigos nas redes sociais. redes, histórico de pedidos em aplicativos de táxi etc. Todos esses casos são unidos pela necessidade de alterar constantemente os dados da lista para novos, quando, por exemplo, o usuário deslizou o dedo para atualizar, filtrou a lista ou de qualquer outra forma recebeu um pacote de novos dados pela parte de trás.


Para implementar esse comportamento, o ancestral do desenvolvedor moderno do Android selecionou manualmente quais dados e como foram alterados e chamou os métodos apropriados do RecyclerView . No entanto, tudo mudou quando o Google lançou a Biblioteca de suporte versão 25.1.0, adicionando o DiffUtil lá, o que permitiu transformar magicamente a lista antiga em uma nova sem reconstruir completamente o RecyclerView . Neste artigo, vou dissipar a magia do DiffUtil e explicar como ele funciona.


Como trabalhar com o DiffUtil?


Para trabalhar com o DiffUtil você deve implementar o DiffUtil.Callback , chamar o método calculateDiff(@NonNull Callback cb) e aplicar o DiffResult recebido ao RecyclerView usando o método dispatchUpdatesTo() . O que acontece quando o calucalteDiff(@NonNull Callback cd) é chamado? Este método retorna um DiffResult , que contém um conjunto de operações para converter a lista original em uma nova. As atualizações são aplicadas por chamadas aos notifyItemRangeInserted , notifyItemRangeRemoved , notifyItemMoved e notifyItemRangeChanged . Os três primeiros métodos alteram a estrutura da lista, ou seja, as posições dos elementos, sem alterar os próprios elementos e sem chamar onBindViewHolder() neles (com a exceção do elemento sendo adicionado). O último altera o próprio elemento e chama onBindViewHolder() para alterar a exibição do elemento.


DiffUtil verifica as duas listas quanto a diferenças usando o algoritmo de Myers, que determina apenas a presença / ausência de alterações, mas não sabe como encontrar o movimento dos elementos. Para fazer isso, o DiffUtil percorre as DiffUtil criadas pelo algoritmo de Myers (mais sobre isso mais tarde) e, em seguida, procura por movimentos. DiffResult é formado para se o algoritmo não verificar o movimento dos elementos e , onde P é o número de elementos adicionados e excluídos.


Algoritmo de Myers


A seguir, será considerada uma explicação do algoritmo de Myers nos dedos, links para explicações matemáticas do algoritmo (assim como outros artigos interessantes sobre o assunto) estarão no final do artigo. Considere duas sequências: BACAAC e CBCBAB. É necessário escrever uma sequência de transformações sobre a primeira sequência, após a qual obtemos a segunda. Escrevemos as seqüências na tabela da seguinte maneira: a lista antiga indicará os primeiros elementos das colunas e a nova lista será os primeiros elementos das linhas.



Risque as células nas quais elementos idênticos de ambas as seqüências se cruzam:



Uma outra tarefa é ir do canto superior esquerdo da matriz para o canto inferior direito no menor número de etapas. Você pode mover-se pelas faces horizontais e verticais. Se você atingir o ponto em que a linha diagonal começa, deverá movê-la, mas o custo de uma etapa é 0. Consequentemente, o custo de uma etapa ao longo das arestas é 1.



A partir do ponto (0; 0), podemos mover para a direita e para baixo. Ao descer, você também deve ir na diagonal. O movimento feito em uma etapa é chamado de cobra, neste caso, duas cobras receberam: (0; 0) -> (0; 1) e (0; 0) -> (1; 2). A seta indica o fim da cobra, ou seja, se após a etapa vertical / horizontal houver uma etapa obrigatória ao longo da diagonal, a seta estará na etapa ao longo da diagonal. A construção completa das cobras do ponto inicial até a final é mostrada abaixo. Alguns caminhos no vídeo foram omitidos porque obviamente não eram os mais curtos.



Como resultado, obtemos vários caminhos mais curtos possíveis, alguns deles são exibidos abaixo.




Como a passagem de uma matriz da extrema esquerda para a extrema direita ajuda a determinar a sequência de ações (script) para converter uma sequência em outra? O que significam etapas horizontais, verticais e diagonais? Um passo na matriz em uma das direções possíveis são as ações na linha antiga:


  • Passo horizontal - excluir da linha antiga
  • Etapa vertical - adicionar à linha antiga
  • Etapa diagonal - sem alteração

Usando o segundo caminho como exemplo, comparamos o caminho e o script resultante. O primeiro passo é vertical, o que significa que adicionamos o caractere "C" à posição 0 na linha antiga.



No entanto, esta não é a cobra inteira. Em seguida, devemos nos mover na diagonal. Ao se mover na diagonal, o elemento B permanece inalterado. Como resultado, a cobra consiste em movimento vertical + movimento diagonal.



Em seguida, a cobra horizontalmente - remova o elemento A. da linha antiga



O vídeo mostra o caminho inteiro do começo ao fim com uma alteração na cadeia de origem até que seja convertida na final.



O resultado do algoritmo de Myers é um script com um conjunto do número mínimo de ações que devem ser executadas para converter uma sequência em outra. No DiffUtil o algoritmo Myers é usado para procurar diferentes elementos que são determinados pelo método areItemsTheSame() . Além de formar uma lista de cobras, ao passar por listas, o algoritmo Myers cria listas de status de elementos das listas antigas e novas. Todos esses dados, assim como o sinalizador detectMoves e o retorno de chamada implementado pelo usuário, são passados ​​para o construtor DiffResult(Callback callback, List<Snake> snakes, int[] oldItemStatuses, int[] newItemStatuses, boolean detectMoves) .


Enquanto escrevia este artigo, pude descobrir o que exatamente está acontecendo no DiffResult : o algoritmo passa pelas cobras e define sinalizadores para os elementos (nas listas de status), que determinam o que exatamente aconteceu com o elemento. Usando esses sinalizadores, ao aplicar alterações ao RecyclerView é determinado qual método aplicar atualizações: notifyItemRangeInserted, notifyItemRangeRemoved, notifyItemMoved notifyItemRangeChanged . Em mais detalhes, falarei sobre isso na próxima vez.


Referências


Source: https://habr.com/ru/post/pt460673/


All Articles