Menu Docs
Página inicial do Docs
/ /
Atlas Device SDKs
/

React a alterações - Java SDK

Nesta página

  • Atualização automática
  • Registrar um ouvinte de alteração de Realm
  • Registrar um ouvinte de alteração de coleção
  • Registrar um ouvinte de alteração de objeto
  • Cancelar o registro de um ouvinte de alterações
  • Use o Realm em aplicativos do sistema em ROMs personalizadas
  • Alterar limites de notificação

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.

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.

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;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
realm = Realm.getDefaultInstance();
realmListener = new RealmChangeListener<Realm>() {
@Override
public void onChange(Realm realm) {
// ... do something with the updates (UI, etc.) ...
}
};
// Observe realm notifications.
realm.addChangeListener(realmListener);
}
@Override
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.

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)

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."
}

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().

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:

  1. Puxe a política atual do dispositivo:

    adb pull /sys/fs/selinux/policy
  2. Copie o erro do SELinux dentro de um arquivo de texto chamado input.txt.

  3. Execute a ferramenta audit2allow :

    audit2allow -p policy -i input.txt
  4. 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.

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.

Voltar

Segmentação