Modo leitor ou despacho em primeiro plano: qual um aplicativo Android deve usar?
O modo leitor, para uma tela cujo objetivo é ler tags. Ele entrega cada tag direto a um retorno de chamada, consegue pular a conferência de NDEF e o som do sistema, e suspende a emulação de cartão. O despacho em primeiro plano serve a um aplicativo que encontra tags de vez em quando e as quer como intents.
O que os dois fazem
Os dois dão à atividade que está na tela o primeiro direito sobre a próxima tag, na frente de todo aplicativo que se registrou para tags no manifesto dele. A referência de NfcAdapter descreve o despacho em primeiro plano como algo que dá prioridade à atividade em primeiro plano, e o modo leitor como algo que limita o controlador enquanto a atividade está em primeiro plano.
Nenhum dos dois sobrevive à atividade sair da tela. O despacho em primeiro plano tem de ser ativado enquanto a atividade está retomada e desativado antes que o onPause dela termine, e chamá-lo enquanto a atividade não está em primeiro plano levanta IllegalStateException.
E nenhum dos dois muda o que acontece quando o aplicativo está fechado. Uma tag que deve abrir o aplicativo passa pelo manifesto, coberto na página do manifesto.
O que o modo leitor muda
O modo leitor transforma o controlador num leitor e em nada mais. A referência afirma que ele desativa o ponto a ponto e a emulação de cartão enquanto estiver rodando, então um usuário que abre a sua tela de leitura num caixa não consegue pagar com o celular até sair dela.
Ele sonda só as tecnologias que você nomeia, por sinalizadores como FLAG_READER_NFC_A ou FLAG_READER_NFC_V. Uma tela feita para rótulos ISO/IEC 15693 que sonda só NFC-V não reage a um cartão bancário mantido ao lado dela.
FLAG_READER_SKIP_NDEF_CHECK impede a plataforma de ler a tag em busca de NDEF antes que o seu retorno de chamada rode. A referência é explícita sobre o custo: a classe Ndef deixa então de ser enumerada na tag, e o despacho baseado em NDEF não funciona. É o sinalizador de uma tela que envia os próprios comandos, e o sinalizador errado para uma tela que lê mensagens.
FLAG_READER_NO_PLATFORM_SOUNDS silencia o som de descoberta do sistema, o que deixa o seu aplicativo responsável por dizer ao usuário que alguma coisa aconteceu. O extra EXTRA_READER_PRESENCE_CHECK_DELAY define o intervalo que a plataforma usa para conferir que a tag continua ali.
As tags chegam por onTagDiscovered em vez de chegar como intents. A partir do nível de API 37 o retorno de chamada também tem onTagLost, chamado quando a tag sai; antes desse nível, uma saída aparece como uma exceção na troca seguinte.
Para um celular que emula um cartão pela emulação de cartão pelo host, a referência recomenda FLAG_READER_NFC_A junto com FLAG_READER_SKIP_NDEF_CHECK.
O que o despacho em primeiro plano mantém
O despacho em primeiro plano muda quem recebe a tag e pouco mais. A plataforma continua lendo a tag em busca de NDEF e continua tocando o som de descoberta dela, o sinalizador que o silencia pertencendo ao modo leitor, e nada na referência suspende a emulação de cartão enquanto ele está ativado.
A tag chega como uma intent, pelo PendingIntent que você passa. O guia de NFC avançado o monta com FLAG_MUTABLE, porque o sistema tem de acrescentar a tag à intent, e desde o Android 12 a referência de PendingIntent exige que todo PendingIntent declare a mutabilidade dele. O mesmo exemplo acrescenta FLAG_ACTIVITY_SINGLE_TOP, para que a tag chegue à atividade aberta em onNewIntent em vez de abrir uma segunda cópia.
Os filtros e as listas de tecnologia estreitam o que a atividade recebe, com as mesmas regras do manifesto. Passar null nos dois faz a atividade receber toda tag por ACTION_TAG_DISCOVERED, uma ação que a referência marca como descontinuada a partir do nível de API 37.
A entrega cai em onNewIntent, e o guia de processos e threads diz que os retornos de chamada do ciclo de vida rodam sempre na thread principal. Toda troca com a tag tem de sair dali, já que a referência de TagTechnology proíbe entrada e saída nessa thread.
Escolher entre os dois
| Pergunta | Modo leitor | Despacho em primeiro plano |
|---|---|---|
| Como a tag chega | Um retorno de chamada, onTagDiscovered | Uma intent, em onNewIntent |
| Emulação de cartão enquanto ativo | Suspensa | Inalterada |
| Tecnologias sondadas | Só as nomeadas por sinalizadores | Todas |
| Conferência de NDEF e som do sistema | Dá para pular | Sempre feitas |
| Disponível a partir de | Nível de API 19 | Nível de API 10 |
Escolha o modo leitor quando a tela existe para ler tags: uma conferência de estoque, uma ferramenta de configuração, um leitor para um cartão proprietário. A tag vai direto para o seu código e nada mais no celular compete por ela.
Escolha o despacho em primeiro plano quando as tags são uma entrada entre outras, e o celular deve continuar pagando e se comportando normalmente enquanto a sua atividade está aberta.
Perguntas frequentes
Por que o celular para de pagar enquanto o meu aplicativo está aberto?
O modo leitor está ativado. A referência afirma que ele desativa a emulação de cartão enquanto a atividade está em primeiro plano, então reserve-o para telas cujo objetivo é ler.
Que sinalizadores usar para ler outro celular?
Para um celular que emula um cartão pela emulação de cartão pelo host, a referência recomenda FLAG_READER_NFC_A junto com FLAG_READER_SKIP_NDEF_CHECK.
Ainda preciso de filtros de intent no manifesto?
Só para as tags que devem abrir o aplicativo quando ele não está aberto. Nem o modo leitor nem o despacho em primeiro plano valem antes de uma atividade estar na tela.
Por que enableForegroundDispatch levanta IllegalStateException?
Ele foi chamado enquanto a atividade não estava em primeiro plano, por exemplo a partir de onCreate. Ative-o em onResume e desative-o em onPause.