ActivityNotFoundException em Android: Como Reproduzir e Corrigir
Uma técnica de depuração real usando um emulador AOSP
As atividades são uma das estruturas fundamentais de um aplicativo Android. Cada app tem pelo menos uma atividade como ponto de entrada. Enquanto ActivityNotFoundException é fácil de corrigir, reproduzir essa exceção durante o desenvolvimento pode ser surpreendentemente difícil. Neste artigo, mostrarei a você como fazer isso.
O que é ActivityNotFoundException?
Quando tentamos iniciar uma atividade através de Intent e não encontramos nenhuma atividade para executar essa ação específica no dispositivo Android, enfrentaremos esta exceção. E isso ocorrerá principalmente em casos de Intents Implícitos. Apenas definindo do documento oficial do Android abaixo
- Intents Implícitos não especificam uma componente específica, mas em vez disso declaram uma ação geral para executar, o que permite que um componente de outro aplicativo a manipule. Por exemplo, se você quiser mostrar ao usuário uma localização no mapa, pode usar um intent implícito para solicitar que outro aplicativo compatível exiba.
O compilador pode detectar se uma atividade está disponível para Intents Explícitos. Por exemplo,
startActivity(Intent(this, SplashActivity::class.java))me dá um erro de compilação no Android Studio se não houver tal atividade criada, como mostrado em vermelho na imagem abaixo
Mas no caso de um intent implícito, já que não especificamos explicitamente o nome da atividade como abaixo,
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse("https://www.google.com")
startActivity(i)não teremos um erro de compilação, mas há uma chance de obter uma exceção em tempo de execução, que é android.content.ActivityNotFoundException. Isso significa que não há nenhum componente ou atividade específico presente no telefone Android para executar a ação Intent Intent.ACTION_VIEW. Mais informações podem ser encontradas aqui: https://developer.android.com/guide/components/intents-filters
Como podemos reproduzir essa exceção?
Vamos considerar o exemplo de código abaixo:
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse("https://www.google.com")
startActivity(i)Este código simplesmente tenta abrir o endereço da web do Google em um navegador. Se eu tentar executar este código no meu dispositivo Xiaomi pessoal, não encontrarei nenhum problema, pois meu dispositivo já possui APIs do Google e navegadores padrão como o Chrome.
A maioria dos dispositivos de produção já contém um navegador, por isso muitos desenvolvedores nunca encontram este crash durante a fase de teste. Ele geralmente aparece em emuladores AOSP, dispositivos empresariais, ROMs personalizadas ou dispositivos altamente restritos.
Então, como reproduzir?
Para reproduzir este crash, precisaremos de um emulador Android sem APIs do Google. Isso significa um emulador AOSP Android como mostrado no vídeo abaixo.
Depois de criar o emulador, precisaremos verificar se há algum navegador disponível. Devemos notar que o crash pode ser reproduzido apenas quando o emulador não tem um navegador. O meu emulador tem uma WebView Shell.
Devemos desabilitar o aplicativo WebView Shell indo para Configurações -> Pesquisar por web -> WebView Shell -> DESABILITAR.
Uma vez feito isso, se executarmos o aplicativo, reproduziremos facilmente a exceção e o logcat confirma que
Sem desabilitar este aplicativo WebView Shell, a página web será exibida no emulador sem nenhum problema, como mostrado na imagem abaixo
Agora que podemos reproduzir o problema, a correção é simples
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse("https://www.google.com")
if (i.resolveActivity(packageManager) != null) {
startActivity(i)
}resolveActivity() verifica com o Android's PackageManager se há pelo menos uma atividade exportada capaz de lidar com este intent. Se nenhuma for encontrada, retorna null, permitindo que evitemos ActivityNotFoundException.
Este código verifica se há um aplicativo adequado no dispositivo Android que pode lidar com a ação de intent Intent.ACTION_VIEW. No nosso caso, um navegador. Se tal aplicativo for encontrado, apenas a página web será aberta. Mas em vez de lidar com o fluxo positivo ou feliz, é melhor exibir uma mensagem ao usuário durante o fluxo negativo, isto é, quando o navegador não está disponível ou desativado. Então é melhor escrever nosso código assim:
val i = Intent(Intent.ACTION_VIEW)
i.data = Uri.parse("https://www.google.com")
if (i.resolveActivity(packageManager) != null) {
startActivity(i)
} else {
Toast.makeText(this, "Não foi possível abrir a página web. Por favor, instale um navegador e tente novamente.",
Toast.LENGTH_LONG).show()
}Principais pontos a considerar
Sempre verifique se existe um aplicativo para lidar com um intent implícito antes de chamar startActivity(). Esta simples verificação pode prevenir falhas em produção onde o aplicativo necessário está indisponível ou desativado.

