Como criar um projeto impecável

Mesmo que o app seja rápido e responsivo, algumas decisões de design ainda podem causar problemas para os usuários, devido a interações não planejadas com outros aplicativos ou caixas de diálogo, perda acidental de dados, bloqueio não intencional e assim por diante. Para evitar esses problemas, é útil entender o contexto em que seus aplicativos são executados e as interações do sistema que podem afetá-los. Em resumo, você precisa se esforçar para desenvolver um aplicativo que interaja perfeitamente com o sistema e com outros aplicativos.

Um problema comum de integração é quando o processo em segundo plano de um aplicativo, por exemplo, um serviço ou broadcast receiver, abre uma caixa de diálogo em resposta a algum evento. Isso pode parecer um comportamento inofensivo, especialmente quando você está criando e testando o aplicativo isoladamente no emulador. No entanto, quando o aplicativo é executado em um dispositivo real, ele pode não ter o foco do usuário no momento em que o processo em segundo plano exibe a caixa de diálogo. Assim, o aplicativo pode mostrar a caixa de diálogo por trás do aplicativo ativo ou pode tirar o foco do aplicativo atual e mostrar a caixa de diálogo na frente do que o usuário estava fazendo (como discar uma chamada telefônica, por exemplo). Esse comportamento não funcionaria para seu aplicativo ou usuário.

Para evitar esses problemas, o aplicativo precisa usar o recurso de sistema adequado para notificar o usuário: as classes Notification. Com notificações, seu aplicativo pode sinalizar ao usuário que um evento ocorreu, exibindo um ícone na barra de status em vez de focar e interromper o usuário.

Outro exemplo de problema de integração é quando uma atividade perde inadvertidamente o estado ou os dados do usuário porque não implementa corretamente o onPause() e outros métodos do ciclo de vida. Ou, se o aplicativo expõe dados a serem usados por outros aplicativos, exponha-os por meio de um ContentProvider, em vez de fazer isso, por exemplo, usando um banco de dados ou arquivo bruto legível.

O que esses exemplos têm em comum é que envolvem uma boa cooperação com o sistema e outros aplicativos. O sistema Android foi projetado para tratar aplicativos como um tipo de federação de componentes com acoplamento flexível, em vez de blocos de código de caixa preta. Isso permite que você, como desenvolvedor, veja todo o sistema como uma federação ainda maior desses componentes. Isso beneficia você, permitindo uma integração limpa e perfeita a outros aplicativos. Por isso, crie seu próprio código para retribuir o favor.

Este documento discute problemas comuns de continuidade e como evitá-los.

Não descarte dados

Tenha sempre em mente que o Android é uma plataforma para dispositivos móveis. Pode parecer óbvio, mas é importante lembrar que outra atividade (como o app "Chamada telefônica recebida") pode aparecer sobre sua própria atividade a qualquer momento. Isso acionará os métodos onSaveInstanceState() e onPause() e provavelmente resultará no encerramento do seu aplicativo.

Se o usuário estava editando dados no seu app quando a outra atividade apareceu, é provável que seu app perca esses dados quando for encerrado. A menos, é claro, que você salve o trabalho em andamento primeiro. O "jeito Android" é fazer exatamente isso: os aplicativos Android que aceitam ou editam a entrada precisam modificar o método onSaveInstanceState() e salvar o estado de alguma forma adequada. Quando a usuária acessa novamente o aplicativo, ela precisa conseguir recuperar os dados.

Um exemplo clássico do bom uso desse comportamento é um aplicativo de e-mail. Se o usuário estava escrevendo um e-mail quando outra atividade foi iniciada, o aplicativo precisará salvar o e-mail em andamento como rascunho.

Não exponha dados brutos

Se você não andaria na rua usando roupas íntimas, seus dados também não. Embora seja possível expor determinados tipos de aplicativo ao mundo para leitura, essa geralmente não é a melhor ideia. A exposição de dados brutos exige que outros aplicativos entendam o formato dos dados. Se você alterar esse formato, todos os outros aplicativos que não foram atualizados também serão corrompidos.

O "jeito Android" é criar um ContentProvider para expor seus dados a outros aplicativos por uma API limpa, bem pensada e de fácil manutenção. O uso de um ContentProvider é muito parecido com a inserção de uma interface em linguagem Java para dividir e componentes de duas partes de código fortemente acopladas. Isso significa que você poderá modificar o formato interno dos seus dados sem mudar a interface exposta pelo ContentProvider e sem afetar outros aplicativos.

Não interrompa o usuário

Se o usuário estiver executando um aplicativo (como o aplicativo Telefone durante uma chamada), é bem provável que ele tenha feito isso de propósito. É por isso que você precisa evitar atividades de geração, exceto em resposta direta à entrada do usuário da atividade atual.

Ou seja, não chame startActivity() de BroadcastReceivers ou serviços em execução em segundo plano. Isso vai interromper qualquer aplicativo que esteja em execução no momento e deixar o usuário irritado. Talvez ainda pior, sua atividade pode se tornar um "bandido de pressionamento de tecla" e receber algumas das entradas que o usuário estava no meio de fornecer para a atividade anterior. Dependendo do que seu aplicativo faz, isso pode ser uma má notícia.

Em vez de gerar IUs de atividade diretamente do segundo plano, use o NotificationManager para definir notificações. Elas aparecerão na barra de status, e o usuário poderá clicar nelas quando quiser para ver o que o aplicativo precisa mostrar a ele.

Observe que tudo isso não se aplica aos casos em que sua atividade já está em primeiro plano: nesse caso, o usuário espera ver sua próxima atividade em resposta à entrada.

Tem muito a fazer? Faça em uma linha de execução

Se o aplicativo precisar executar alguma computação cara ou de longa duração, provavelmente, será necessário movê-lo para uma linha de execução. Isso evitará que a temida caixa de diálogo "O aplicativo não está respondendo" seja exibida ao usuário, com o resultado final sendo o encerramento ardente do seu aplicativo.

Por padrão, todo o código em uma atividade, assim como todas as visualizações dela, é executado na mesma linha de execução. Essa é a mesma linha de execução que gerencia eventos da IU. Por exemplo, quando o usuário pressiona uma tecla, um evento de pressionamento de tecla é adicionado à fila da linha de execução principal da atividade. O sistema de manipulador de eventos precisa desenfileirar e processar esse evento rapidamente. Caso contrário, o sistema concluirá, após alguns segundos, que o aplicativo está suspenso e se oferecerá para eliminá-lo para o usuário.

Se você tem um código de longa duração, quando executado in-line na sua atividade, ele é executado na linha de execução do manipulador de eventos, o que bloqueia efetivamente o manipulador de eventos. Isso atrasa o processamento de entrada e resulta nas caixas de diálogo de ANR. Para evitar isso, mova seus cálculos para uma linha de execução. O documento Design para capacidade de resposta descreve como fazer isso.

Não sobrecarregue uma única tela de atividade

Qualquer aplicativo usável provavelmente terá várias telas diferentes. Ao projetar as telas da interface, use várias instâncias do objeto Activity.

Dependendo do seu histórico de desenvolvimento, é possível interpretar uma atividade como semelhante a um miniaplicativo Java, porque ela é o ponto de entrada do aplicativo. No entanto, isso não é muito preciso: quando uma subclasse do miniaplicativo é o ponto de entrada único para um miniaplicativo Java, uma atividade precisa ser considerada um dos possíveis vários pontos de entrada para seu aplicativo. A única diferença entre sua atividade "principal" e as outras que você possa ter é que a atividade "principal" é a única que demonstrou interesse na ação "android.intent.action.MAIN" no seu arquivo AndroidManifest..xml.

Portanto, ao projetar seu aplicativo, pense nele como uma federação de objetos de atividade. Isso tornará seu código muito mais sustentável a longo prazo e, como um bom efeito colateral, também funcionará bem com o histórico de aplicativos e o modelo de "pilha de retorno" do Android.

Estenda temas do sistema

Quando se trata da aparência da interface do usuário, é importante combinar bem. Os usuários são desagradáveis com aplicativos que contrastam com a interface que eles esperam. Ao projetar suas interfaces, tente evitar usar suas próprias interfaces o máximo possível. Em vez disso, use um tema. Você pode substituir ou estender as partes do tema que precisa, mas, pelo menos, você está começando pela mesma base de IU que todos os outros aplicativos. Para saber mais, consulte Estilos e temas.

Projete sua interface para funcionar com várias resoluções de tela

Diferentes dispositivos com tecnologia Android serão compatíveis com resoluções de tela diferentes. Alguns poderão até mudar a resolução rapidamente, por exemplo, alternando para o modo paisagem. É importante garantir que seus layouts e drawables sejam flexíveis o suficiente para serem exibidos corretamente em várias telas de dispositivos.

Felizmente, é muito fácil fazer isso. Resumindo, você precisa fornecer versões diferentes da arte (se usar alguma) para as principais resoluções e, em seguida, projetar o layout para acomodar várias dimensões. Por exemplo, evite usar posições codificadas. Em vez disso, use layouts relativos. Se você fizer isso, o sistema vai cuidar do resto, e seu aplicativo terá uma ótima aparência em qualquer dispositivo.

Prepare-se para uma rede lenta

Os dispositivos Android têm várias opções de conexão de rede. Todos terão alguma provisão de acesso a dados, embora alguns sejam mais rápidos que outros. No entanto, o menor denominador comum é o GPRS, o serviço de dados não 3G para redes GSM. Até mesmo dispositivos compatíveis com 3G passarão muito tempo em redes não 3G, então redes lentas continuarão sendo uma realidade por muito tempo.

É por isso que você deve sempre codificar seus aplicativos para minimizar o acesso à rede e a largura de banda. Não se pode presumir que a rede seja rápida, então sempre se planeje para que ela seja lenta. Se os usuários estiverem em redes mais rápidas, isso é ótimo. A experiência deles só vai melhorar. Evite o caso inverso: aplicativos que podem ser usados algumas vezes, mas frustrantemente nos outros, com base em onde o usuário está em um determinado momento, provavelmente não são muito conhecidos.

Um possível problema é que é muito fácil cair nessa armadilha se você estiver usando o emulador, já que ele usa a conexão de rede do computador desktop. É quase garantido que ele será muito mais rápido que uma rede de celular. Portanto, convém alterar as configurações no emulador que simulam velocidades de rede mais lentas. Você pode fazer isso no Android Studio pelo AVD Manager ou com uma opção de linha de comando ao iniciar o emulador.

Não conte com touchscreen ou teclado

O Android é compatível com vários formatos de dispositivos. Essa é uma maneira sofisticada de dizer que alguns dispositivos Android terão teclados "QWERTY" completos, enquanto outros terão 40 teclas, 12 teclas ou até mesmo outras configurações de teclas. Da mesma forma, alguns dispositivos terão touchscreen, mas muitos não.

Ao criar seus aplicativos, lembre-se disso. Não faça suposições sobre layouts de teclado específicos, a menos que você tenha interesse em restringir seu aplicativo para que ele só possa ser usado nesses dispositivos.

Preserve a bateria do dispositivo

Um dispositivo móvel não é muito móvel se está constantemente conectado à parede. Os dispositivos móveis são alimentados por bateria, e quanto mais ela durar com uma carga, mais feliz todos ficam, especialmente o usuário. Dois dos maiores consumidores de energia da bateria são o processador e o rádio. É por isso que é importante programar seus aplicativos para fazer o mínimo de trabalho possível e usar a rede com a menor frequência possível.

Minimizar a quantidade de tempo de processador usada pelo aplicativo se resume a programar códigos eficientes. Para minimizar o consumo de energia do uso do rádio, lide com as condições de erro da maneira correta e busque apenas o que você precisa. Por exemplo, não tente repetir constantemente uma operação de rede se uma delas falhar. Se ele falhou uma vez, é provavelmente porque o usuário não tem sinal. Portanto, ele provavelmente vai falhar de novo se você tentar imediatamente. Você só vai desperdiçar energia da bateria.

Os usuários são muito inteligentes: se o seu programa consumir muita bateria, você pode contar que eles perceberão. A única coisa de que você pode ter certeza nesse momento é que o programa não ficará instalado por muito tempo.