React a alterações - Java SDK
Nesta página
Os objetos em clientes do Realm são objetos ativos que são atualizados automaticamente para refletir as alterações de dados, incluindo alterações remotassincronizadas , e emitem eventos de notificação que você pode assinar sempre que os dados subjacentes forem alterados.
Qualquer aplicativo moderno deve ser capaz de React quando os dados mudam, independentemente da origem dessa alteração. Quando um usuário adiciona um novo item a uma lista, você pode querer atualizar a UI, mostrar uma notificação ou registrar uma mensagem. Quando alguém atualiza esse item, você pode querer alterar seu estado visual ou disparar uma solicitação de rede. Por fim, quando alguém exclui o item, você provavelmente quer removê-lo da UI. O sistema de notificação do Realm permite observar e React a alterações em seus dados, independentemente das gravações que causaram as alterações.
O Realm emite três tipos de notificações:
Realm de domínio sempre que um domínio específico confirma uma transação de escrita.
Notificações de coleção sempre que qualquer Objeto de Realm em uma coleção for alterado, incluindo inserções, atualizações e exclusões.
Notificações de objeto sempre que um objeto específico do Realm for alterado, incluindo atualizações e exclusões.
Atualização automática
Objetos de Realm acessados em um thread associado a um Looper atualizado automaticamente periodicamente para refletir as alterações nos dados subjacentes.
O thread da UI do Android sempre contém uma instância Looper
. Se você precisar manter Objeto de Realm por longos períodos de tempo em qualquer outro thread, deverá configurar um Looper
para esse thread.
Aviso
Sempre feche as Instância de Realm em threads de loop de eventos para evitar vazamentos de recursos
Realms em uma thread sem um Looper não avance automaticamente a versão. Isso pode aumentar o tamanho do Realm na memória e no disco. Evite usar instâncias de Realm em threads que não sejam do Looper quando possível. Se você abrir um Realm em uma thread que não seja do Looper, feche o Realm quando terminar de usá-lo.
Registrar um ouvinte de alteração de Realm
Você pode registrar um manipulador de notificações em um Realm inteiro. O Realm chama o manipulador de notificações sempre que qualquer transação de escrita envolvendo esse Realm é confirmada. O manipulador não recebe informações sobre a mudança.
Isso é útil quando você quer saber que houve uma alteração, mas não quer saber especificamente o que mudou. Por exemplo, aplicativos de prova de conceito geralmente usam esse tipo de notificação e simplesmente atualizam toda a interface do usuário quando algo muda. À medida que o aplicativo se torna mais sofisticado e sensível ao desempenho, os desenvolvedores do aplicativo mudam para notificações mais granulares.
Exemplo
Suponha que você esteja escrevendo um aplicativo de colaboração em tempo real. Para dar a impressão de que seu aplicativo está cheio de atividades de colaboração, você deseja ter um indicador que acenda quando alguma alteração é feita. Nesse caso, um manipulador de notificações de domínio seria uma ótima maneira de orientar o código que controla o indicador. O código a seguir mostra como observar um domínio para alterações com addChangeListener():
public class MyActivity extends Activity { private Realm realm; private RealmChangeListener realmListener; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); realm = Realm.getDefaultInstance(); realmListener = new RealmChangeListener<Realm>() { public void onChange(Realm realm) { // ... do something with the updates (UI, etc.) ... } }; // Observe realm notifications. realm.addChangeListener(realmListener); } protected void onDestroy() { super.onDestroy(); // Remove the listener. realm.removeChangeListener(realmListener); // Close the Realm instance. realm.close(); } }
class MyActivity : Activity() { private lateinit var realm: Realm private lateinit var realmListener: RealmChangeListener<Realm> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) realm = Realm.getDefaultInstance() realmListener = RealmChangeListener { // ... do something with the updates (UI, etc.) ... } // Observe realm notifications. realm.addChangeListener(realmListener) } override fun onDestroy() { super.onDestroy() // Remove the listener. realm.removeChangeListener(realmListener) // Close the Realm instance. realm.close() } }
Importante
Atualização automática
Todos os threads que contêm um Looper
atualizam automaticamente as instâncias RealmObject
e RealmResult
quando novas alterações são gravadas no Realm. Como resultado, não é necessário buscar esses objeto novamente ao React a um RealmChangeListener
, pois esses objeto já estão atualizados e prontos para serem redesenhados na tela.
Registrar um ouvinte de alteração de coleção
Você pode registrar um manipulador de notificações em uma collection específica dentro de um Realm. O manipulador recebe uma descrição das alterações desde a última notificação. Especificamente, esta descrição consiste em três listas de índices:
Os índices dos objetos que foram excluídos.
Os índices dos objetos que foram inseridos.
Os índices dos objetos que foram modificados.
Pare a entrega de notificações ligando para os métodos removeChangeListener()
ou removeAllChangeListeners()
. As notificações também param se:
o objeto no qual o ouvinte está registrado recebe a coleta de lixo.
a instância de realm fecha.
Mantenha uma forte referência ao objeto que você está ouvindo pelo tempo que precisar das notificações.
Importante
Questões importantes
Em manipuladores de notificações de collection, sempre execute alterações na seguinte ordem: exclusões, inserções e modificações. Manipular inserções antes de exclusões pode resultar em comportamento inesperado.
O Realm emite uma notificação inicial após recuperar a collection. Depois disso, o Realm entrega notificações de collection de forma assíncrona sempre que uma transação de escrita adiciona, altera ou remove objetos na collection.
Ao contrário das notificações de domínio, as notificações de coleção contêm informações detalhadas sobre a alteração. Isso permite respostas sofisticadas e seletivas às mudanças. As notificações de collection fornecem todas as informações necessárias para managed uma lista ou outra visualização que represente a collection na interface do usuário.
O código a seguir mostra como observar alterações em uma coleção com addChangeListener():
RealmResults<Dog> dogs = realm.where(Dog.class).findAll(); // Set up the collection notification handler. OrderedRealmCollectionChangeListener<RealmResults<Dog>> changeListener = (collection, changeSet) -> { // For deletions, notify the UI in reverse order if removing elements the UI OrderedCollectionChangeSet.Range[] deletions = changeSet.getDeletionRanges(); for (int i = deletions.length - 1; i >= 0; i--) { OrderedCollectionChangeSet.Range range = deletions[i]; Log.v("EXAMPLE", range.length + " dogs deleted at " + range.startIndex); } OrderedCollectionChangeSet.Range[] insertions = changeSet.getInsertionRanges(); for (OrderedCollectionChangeSet.Range range : insertions) { Log.v("EXAMPLE", range.length + " dogs inserted at " + range.startIndex); } OrderedCollectionChangeSet.Range[] modifications = changeSet.getChangeRanges(); for (OrderedCollectionChangeSet.Range range : modifications) { Log.v("EXAMPLE", range.length + " dogs modified at " + range.startIndex); } }; // Observe collection notifications. dogs.addChangeListener(changeListener);
val dogs = realm.where(Dog::class.java).findAll() // Set up the collection notification handler. val changeListener = OrderedRealmCollectionChangeListener { collection: RealmResults<Dog>?, changeSet: OrderedCollectionChangeSet -> // For deletions, notify the UI in reverse order if removing elements the UI val deletions = changeSet.deletionRanges for (i in deletions.indices.reversed()) { val range = deletions[i] Log.v("EXAMPLE", "${range.length} dogs deleted at ${range.startIndex}") } val insertions = changeSet.insertionRanges for (range in insertions) { Log.v("EXAMPLE", "${range.length} dogs inserted at ${range.startIndex}") } val modifications = changeSet.changeRanges for (range in modifications) { Log.v("EXAMPLE", "${range.length} dogs modified at ${range.startIndex}") } } // Observe collection notifications. dogs.addChangeListener(changeListener)
Registrar um ouvinte de alteração de objeto
Você pode registrar um manipulador de notificações em um objeto específico dentro de um domínio. O realm notifica seu manipulador:
Quando o objeto é excluído.
Quando qualquer uma das propriedades do objeto é alterada.
O manipulador recebe informações sobre quais campos foram alterados e se o objeto foi excluído.
Pare a entrega de notificações ligando para os métodos removeChangeListener()
ou removeAllChangeListeners()
. As notificações também param se:
o objeto no qual o ouvinte está registrado recebe a coleta de lixo.
a instância de realm fecha.
Mantenha uma forte referência do objeto que você está ouvindo pelo tempo que precisar das notificações.
O código a seguir mostra como criar uma nova instância de uma classe em um Realm e observar essa instância para alterações com addChangeListener():
// Create a dog in the realm. AtomicReference<Dog> dog = new AtomicReference<Dog>(); realm.executeTransaction(transactionRealm -> { dog.set(transactionRealm.createObject(Dog.class, new ObjectId())); dog.get().setName("Max"); }); // Set up the listener. RealmObjectChangeListener<Dog> listener = (changedDog, changeSet) -> { if (changeSet.isDeleted()) { Log.i("EXAMPLE", "The dog was deleted"); return; } for (String fieldName : changeSet.getChangedFields()) { Log.i("EXAMPLE", "Field '" + fieldName + "' changed."); } }; // Observe object notifications. dog.get().addChangeListener(listener); // Update the dog to see the effect. realm.executeTransaction(r -> { dog.get().setName("Wolfie"); // -> "Field 'name' was changed." });
// Create a dog in the realm. var dog = Dog() realm.executeTransaction { transactionRealm -> dog = transactionRealm.createObject(Dog::class.java, ObjectId()) dog.name = "Max" } // Set up the listener. val listener = RealmObjectChangeListener { changedDog: Dog?, changeSet: ObjectChangeSet? -> if (changeSet!!.isDeleted) { Log.i("EXAMPLE", "The dog was deleted") } else { for (fieldName in changeSet.changedFields) { Log.i( "EXAMPLE", "Field '$fieldName' changed." ) } } } // Observe object notifications. dog.addChangeListener(listener) // Update the dog to see the effect. realm.executeTransaction { r: Realm? -> dog.name = "Wolfie" // -> "Field 'name' was changed." }
Cancelar o registro de um ouvinte de alterações
Você pode cancelar o registro de um ouvinte de alterações passando seu ouvinte de alterações para Realm.removeChangeListener(). Você pode cancelar o registro de todos os ouvintes de alterações atualmente inscritos em alterações em um domínio ou qualquer um de seus objetos ou coleções vinculados com Realm.removeAllChangeListeners().
Use o Realm em aplicativos do sistema em ROMs personalizadas
O Realm usa pipe nomeados para oferecer suporte a notificações e acesso ao Arquivo de Realm a partir de vários processos. Embora isso seja permitido por padrão para aplicativos normais de usuário, não é permitido para aplicativos do sistema.
Você pode definir aplicativos do sistema definindo android:sharedUserId="android.uid.system"
no manifesto do Android. Ao trabalhar com um aplicativo do sistema, você pode ver uma violação de segurança no Logcat que se parece com algo assim:
05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:99): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0 05-24 14:08:08.984 6921 6921 W .realmsystemapp: type=1400 audit(0.0:100): avc: denied { write } for name="realm.testapp.com.realmsystemapp-Bfqpnjj4mUvxWtfMcOXBCA==" dev="vdc" ino=14660 scontext=u:r:system_app:s0 tcontext=u:object_r:apk_data_file:s0 tclass=dir permissive=0
Para corrigir isso, é necessário ajustar as regras de segurança do SELinux na ROM. Isso pode ser feito usando a ferramenta audit2allow
, fornecida como parte do AOSP:
Puxe a política atual do dispositivo:
adb pull /sys/fs/selinux/policy Copie o erro do SELinux dentro de um arquivo de texto chamado input.txt.
Execute a ferramenta
audit2allow
:audit2allow -p policy -i input.txt A ferramenta deve gerar uma regra que você pode adicionar à sua política existente para habilitar o uso do Realm.
Um exemplo dessa política é fornecido abaixo:
# Allow system_app to create named pipes required by Realm # Credit: https://github.com/mikalackis/platform_vendor_ariel/blob/master_oreo/sepolicy/system_app.te allow system_app fuse:fifo_file create; allow system_app system_app_data_file:fifo_file create; allow system_app system_app_data_file:fifo_file { read write }; allow system_app system_app_data_file:fifo_file open;
Dica
Veja também: audit2allow
audit2allow
é produzido ao compilar AOSP/ROM e é executado apenas no Linux. Você pode ler mais sobre isso aqui.
Observação
Alterações no Android Oreo e superior
Desde o Android Oreo, o Google mudou a forma como configura o SELinux. As políticas de segurança padrão agora são muito mais modularizadas. Leia mais sobre isso aqui.
Alterar limites de notificação
Alterações em documentos aninhados com mais de quatro níveis abaixo não acionam notificações de alterações.
Se você tiver uma estrutura de dados em que precise escutar as alterações em cinco níveis para baixo ou mais profundamente, as soluções alternativas incluem:
Refatore o esquema para reduzir o aninhamento.
Adicione algo como "pressione para atualizar" para permitir que os usuários atualizem os dados manualmente.