Artigos

10 erros que todo desenvolvedor Android deve evitar

Fala pessoal! Nesse post irei explicar alguns dos erros mais comuns cometidos por nós desenvolvedores Android, baseado em leituras e minha experiência em desenvolvimento com a plataforma. Em cada trecho, explico o erro e como devemos fazer para evitá-los.

#1: Utilizar experiencias de outras plataformas

Antes, era muito comum ver uma app Android imitando as experiencias de outras plataformas. Geralmente, por causa de um porting da mesma app existente em iOS ou Windows Phone; ou por exigência do cliente ou por utilizar algum framework cross-platform. Usuários Android querem e esperam experiencias da plataforma Android. Algo como adicionar um botão de voltar em sua app (existe um botão físico próprio para isso, sabia?), utilizar abas estáticas abaixo ou adaptar ícones retangulares com cantos arredondados (a menos que faça parte da sua logo) são apenas alguns exemplos de experiencias que se encaixam muito bem em outras plataformas, porém devem ser evitadas em Android. Aliás, há algum tempo, a Google desenvolveu guidelines que sugerem excelentes dicas e orientam como deve ser o desenvolvimento, design e distribuição de um aplicativo Android.

#2: Não investir em design / UX

Está comprovado que a maioria das pessoas, ao comprar um produto, levam em consideração vários fatores, dentre eles primeiramente está seu design, quer seja retrô ou futurista. Mas por quê isso? Por que chama atenção. É bonito de se ver. E assim deve ser a sua app. Os usuários devem ter prazer de utilizá-la. Como dizia o finado Steve Jobs, um bom produto é aquele que não precisa de manual para se utilizar. Ele deve ser tão intuitivo e minimalista para que as pessoas tenham facilidade de usá-lo. Então, invista em um bom design, desde o ícone da sua app, as imagens principais, secundárias, etc. Se você não tem expertise de design, então contrate um. Mesmo assim caso não conheça, existem sites como 99Designs, onde você informa sua ideia e vários designers projetam logos para o seu produto e você escolhe a que achar melhor!

#3: Deixar sua app “caduca”

Um dos grandes desafios em Android é acompanhar a evolução desenfreada da plataforma. E a cada novidade, surgem mais APIs, novos frameworks, novos design patterns, novos widgets, etc. Porém, acabamos por procrastinar em atualizar as apps com novidades (exemplo como começar a utilizar Fragments em vez de somente Activity) por medo de “quebrar” o que está funcionando, cultura de que “time que está ganhando não se mexe”. Mas tudo isso você só estará contribuindo cada vez mais para o fracasso da sua app. Então não seja seu próprio vilão e aproveite para estudar as novas APIs, tendencias e benefícios trazidos por essas novidades. Saiba que os seus concorrentes estão fazendo isso agora mesmo. E você não quer ficar para trás, não é?!

#4: Impor o usuário a utilizar sua app de uma única maneira

Outro grande erro que vejo desenvolvedores cometerem é exigir que o usuário utilize seu smartphone ou tablet de uma única maneira, travando a orientação. Saiba que, segundo Neto Marin (Developer Advocate da Google Brasil), travar orientação é um dos principais motivos para que sua app não seja destaque na Google Play Store (exceto jogos). Afinal, quem não sonha em querer ver sua app como uma das mais baixadas ou recomendadas pelos editores da loja de aplicativos? 🙂 Então para evitar isso, vão duas dicas essenciais:

Forneça recursos alternativos quando ocorrer mudanças de configuração: Suponha que você possua um layout em res/layout/activity_main.xml. Adicionando o novo layout em res/layout-land/activity_main.xml, o dispositivo irá carregá-lo quando tiver no modo paisagem (landscape). Além disso, com o uso dos qualifiers, é possível fornecer mais layouts alternativos de acordo com vários aspectos da plataforma, como nível de densidade, versão da API, resolução, etc. Confira aqui a lista de qualifiers disponíveis na plataforma.

– Trate orientação de acordo com o ciclo de vida da Activity: Imagine que você está quase terminando de preencher vários campos de um cadastro em alguma app e, durante o preenchimento, decide segurar o seu dispositivo de maneira mais confortável, por exemplo, no modo paisagem. Daí vem a surpresa: o que você estava digitando se apagou e você terá que digitar tudo novamente, mas não com a mesma paciencia de antes, claro! Por que isso aconteceu? Em Android, quando ocorre qualquer mudança na configuração do dispositivo (ex: orientação, idioma, método de entrada, etc), o ciclo de vida é reiniciado para que as mudanças tenham efeito e a tela seja carregada com recursos alternativos para aquela situação, caso existam. Sendo assim, o método onCreate() é chamado novamente.

Ciclo de vida de uma Activity
Ciclo de vida simplificado de uma Activity

Dessa forma, antes da tela ser pausada para exibir a nova tela em outra orientação, é possível salvar informações pendentes (ex: informações de campos de texto, posição de item selecionado na lista, etc) para que os dados não sejam perdidos. Para isso, basta sobrescrever o método onSaveInstanteState(). E sobrescrevendo o método onRestoreInstanceState() é possível recuperar as informações previamente salvas. Os códigos a seguir, mostram um exemplo salvando um texto de um campo de texto e recuperando-o posteriormente.

[code language=”java”]
@Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);

// salvando o texto antes da orientação ocorrer
String texto = campoDeTexto.getText().toString();
outState.putString("texto", texto);
}
[/code]

[code language=”java”]
@Override
public void onRestoreInstanceState(Bundle inState){
super.onRestoreInstanceState(inState);

// texto recuperado durante a transição de orientação de tela
String texto = inState.getString("texto");
campoDeTexto.setText(texto);
}
[/code]

#5: Atualizar todo conteúdo da Activity dinamicamente

Suponha que você precise atualizar o conteúdo da tela de acordo com diferentes contextos. Como você faria? Uma péssima maneira que deve ser evitada a todo custo é o carregamento de layouts distintos para a mesma Activity, conforme o trecho a seguir.

[code language=”java”]
// método chamado ao clicar em alguns botões
public void onClick(View view){
if (view == botao1){
setContentView(R.layout.activity_main1);
// tratar componentes do layout activity_main1.xml

} else if (view == botao2){
setContentView(R.layout.activity_main2);
// tratar componentes do layout activity_main2.xml

} else if (view == botao3){
setContentView(R.layout.activity_main3);
// tratar componentes do layout activity_main3.xml
}
}
[/code]

No código anterior, caso o usuário escolha um botão específico, um novo layout é cattegado. Além disso ser uma má prática de programação, pois aumenta o acoplamento e dificulta a manutenção; toda vez que o conteúdo da tela for alterado, isso pode implicarconsideravelmente na performance da sua app, pois a cada chamada do método setContentView(), a árvore de componentes Java é inflada na memória. Então, veja a seguir duas maneiras adequadas de se atualizar o conteúdo de telas:

– Criar uma Activity por layout: Adaptando o trecho de código anterior, teríamos que criar mais duas Activities para os layouts activity_main2.xml e activity_main3.xml, para encapsular toda a regra de negócio específica de cada tela. Depois, conforme o botão pressionado, basta chamar a tela por meio do método startActivity(). Confira no trecho a seguir como deve ficar.

[code language=”java”]
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onClick(View v){
if (v == botao2){
startActivity(new Intent(this, Activity2.class));

} else if (v == botao3){
startActivity(new Intent(this, Activity3.class));
}
}
}
[/code]

– Utilizar Fragment: A versão Honeycomb (API Level 11) trouxe uma das grandes novidades utilizadas até hoje: os Fragments. Um Fragment nada mais é do que um componente com seu próprio ciclo de vida e que exibe interface gráfica que pode ser reutilizado em outras telas. Dessa maneira, ficou muito mais simples e prático atualizar apenas porções de telas com Fragment, trazendo mais dinamismo para as apps. Para saber mais sobre Fragment, confira este guia passo-a-passo que a Google preparou.

Exemplo da utilização de Fragments
Exemplo da utilização de Fragments

#6: Projetar sua app para apenas um tipo de dispositivo

Já existem mais de bilhões de dispositivos Android no mundo todo, e a cada dia esse número cresce exponencialmente. Dispositivos de 5, 7, 9, 10 polegadas, rodando várias versões de API, com média, alta definição de tela. Porém, você desenvolveu apenas utilizando como referencia seu próprio smartphone e assume que rodará em qualquer outro dispositivo, pois você escolheu a versão mínima do SDK como a menor possível. E quando você vai publicar sua app, chove de reclamações negativas na Google Play sobre incompatibilidade em tablets, imagens distorcidas em telas maiores, espaçamento excessivo entre os componentes, etc. Então, para evitar esse cenário caótico, projete sua app para rodar na maioria dos dispositivos.

Android tem uma particularidade chamada de density-independent pixel (dp),  que representa uma unidade virtual utilizada para dimensões de layouts, posições e espaçamentos, de maneira independente de densidade de tela. Por exemplo, 1dp equivale a 1 pixel físico em uma tela de 160 dpi, que é baseline para uma tela de densidade média. Dessa forma, em tempo de execução, a plataforma irá escalonar transparentemente as unidades de acordo com a densidade de tela. A conversão para pixels é realizada da seguinte maneira:

[code]
px = dp * (dpi / 160)
[/code]

Nas imagens a seguir – retiradas do próprio Android Developer Guide – três layouts com densidade baixa, média e alta são mostrados quando o mecanismo de independência de densidade não é utilizado; e as mesmas telas, quando há esse mecanismo habilitado. Perceba que na segunda imagem, as figuras se mantêm com o mesmo tamanho.

density-test-bad
Telas sem o mecanismo de independência de densidade
density-test-good
Telas com o mecanismo de independência de densidade

A maneira mais adequada de realizar isso, é fornecendo recursos alternativos como drawables, layouts por meio dos qualifiers gerais:

  • ldpi: baixa densidade de tela, aproximadamente 120 dpi (depreciado)
  • mdpi: média densidade de tela, aproximadamente 160 dpi
  • hdpi: alta densidade de tela, aproximadamente 240 dpi
  • xhdpi: muito alta densidade de tela, aproximadamente 320 dpi
  • xxhdpi: 2x muito alta densidade de tela, aproximadamente 480 dpi
  • xxxhdpi: 3x muito alta densidade de tela, aproximadamente 960 dpi

Durante o desenvolvimento de um layout, é possível prever como ele irá ficar nos outros tamanhos de tela, selecionando a visão Preview, clicando no ícone do dispositivo no canto superior  e marcando Preview All Screens.

#7: Ignorar os avisos do Lint

Sabe aquela lâmpada amarela que aparece quando você está criando um layout em XML ou referenciando alguma API em Java? Pois é, são avisos que a ferramenta Lint fornece para que sua app tenha o máximo de compatibilidade em outros dispositivos. Não apenas isso, mas alerta sobre várias situações, como:

  • Chamada de um comando específico de uma API Level (exemplo, v21). Esse trecho irá quebrar caso o dispositivo esteja usando uma API abaixo (exemplo, v14).
  • Ignorar o atributo android:contentDescription de um ImageView, utilizado especialmente para questões de acessibilidade para usuários com deficiência visual
  • Utilização de strings estáticas em XML (hard-code string). Dessa forma, o recurso de internacionalização será prejudicado, pois a string será a mesma independente de idioma
  • Utilização de espaçamento e margem inicial e final para idiomas com suporte de escrita da direita para a esquerda (R-T-L  – Right To Left)
  • Utilização de aninhamento de layouts que pode prejudicar a performance da sua app

No Android Studio, acessando Settings > Inspections > Android Lint, é possível visualizar todas as inspeções que a ferramenta pode realizar.

Configurações do Lint
Configurações do Lint

#8: Não utilizar animações

Animações trazem fluidez, dinamismo e aspectos de realidade para a sua app. Então, utilize-as para dar um feedback visual melhor, como por exemplo, realizar um efeito de esmaecimento  (fade in/fade out), por exemplo, quando as imagens forem carregadas.

Carregar uma animação é muito fácil e existem várias maneiras de se fazer isso. A API de animação é bem extensa e existe uma excelente documentação no Android Developer Guide que explora todas elas. Para realizar um simples efeito de crossfade de uma imagem, seria necessário o trecho a seguir:

[code language=”java”]
ImageView imageView = (ImageView) findViewById(R.id.view);
Animation animation = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
animation.startAnimation(imageView);
[/code]

#9: Bloquear a Thread de UI

Toda ação que é realizada nos métodos de uma Activity ou Fragment são realizados por meio da Thread de UI. Isso significa que, se alguma tarefa pesada for realizada no método onCreate() (lembre-se que nesse momento, o usuário ainda não está interagindo com a app), a arquitetura da plataforma irá exibir a temida tela Application Not Responding (ANR), caso esse processamento demore mais que 5 segundos. Visualmente, o que o usuário irá perceber é que sua app travou e, provavelmente, irá fechar sua app, desinstalar e colocar um comentário não muito agradável na Google Play. Um jeito bem simples de travar sua app é realizando por exemplo uma consulta ao banco de dados interno, que dependendo do tamanho e do poder de processamento dos outros devices dos seus usuários, pode demorar muito mais do que você pensa. Então, para evitar esse transtorno e muitos usuários estressados, realize tarefas pesadas em segundo plano, utilizando Services, AsyncTasks, worker threads juntamente com handlers. Não esqueça sempre de informar o usuário sobre o status do processamento (ex: ProgressDialog, ProgressBar) enquanto as informações estão sendo carregadas. Outra alternativa é utilizar bibliotecas que já fazem tudo isso de forma transparente, como a Picasso, da Square, para carregamento de imagens em segundo plano.

Tela ANR sendo exibida para um processamento muito longo na Thread de UI.
Tela ANR sendo exibida para um processamento muito longo na Thread de UI.

#10: Ignorar o ciclo de vida dos componentes

E o último mas não menos importante erro está relacionado ao fato de alguns desenvolvedores (principalmente os menos experientes) não levarem em consideração o ciclo de vida dos componentes de alto nível: Activity, Broadcast Receiver, Service e Content Provider. Vão aqui algumas dicas essenciais para desenvolver uma app otimizada sem que prejudique a performance:

  • Durante o método onCreate(), não há interação com o usuário (a tela ainda nem está visível). Por isso, ele deve ser utilizado apenas para inicialização de componentes e objetos, como por exemplo inflar os componentes de um layout para serem utilizados em outros métodos futuramente;
  • A partir do método onResume(), há a interação com o usuário, isto é, a UI está totalmente visível e o usuário poderá interagir com a mesma (ex: clicar em botões, realizar eventos de scroll, etc). Dessa forma, implemente trechos que devem ser atualizados a cada vez que a tela estiver para ser exibida, como vincular os dados das preferências nos componentes gráficos, iniciar tarefas pesadas em segundo plano, indicando o status do processamento visualmente, por meio de progressos.
  • O método onPause() é chamado logo que a tela está prestes para ser interrompida. Dessa maneira, aproveite para salvar informações que precisam estar disponíveis assim que o usuário voltar para sua app, como por exemplo, voltar para a fase do game que ele estiver jogando;
  • No método onDestroy() o componente será destruído. Por isso, é uma boa oportunidade para desalocar recursos, como eventos de captura de broadcasts (Intents), atualização de geolocalização, conexão com serviços da Google Play Services, etc;
  • Utilize o método onLowMemory() para deixar sua app em um modo de economia de energia, desalocando recursos que possam consumir muito sua bateria, como conexão de dados ou Wi-Fi, serviço de geolocalização, brilho de tela, etc;
  • Salve informações temporárias durante o método onSaveInstanteState() e recupere no onRestoreInstanceState(), para evitar perda durante a alteração nas configurações do dispositivo (ex: rotacionar uma tela, alterar idioma, alterar modo de entrada, salvar posição de itens na lista, etc)

É isso aí galera, vamos seguir as boas convensões e boas práticas em Android? 🙂 Abraços e até a próxima!

Referências

Android Developer Guide

Android Design Guide

Top 10 Most Common Android Development Mistakes: A Programming Tutorial

7 Comentários

  • jlameira

    Muito Bacana essas dicas, Parabéns a equipe do Tasafo,
    poderia complementar aqui algumas coisas mais sobre a comunicação visual:
    Um projeto bem planejado determinará a longevidade de sua aplicação.

    1-Faça a ativação de seu sistema de forma bem direta: A interação inicial do usuário com o seu app é que vai ajudar no sucesso do seu app. Faça o inicio de seu app tão simples quanto possível. Não faça formulário de inscrição grandes, minimize a entrada de texto pelo usuário. Para não correr o risco de o usuário adiar o uso de seu app.
    2-Faça uma navegação bem intuitiva: Sem uma navegação bem intuitiva, você fará com que o usuário perca o interesse pelo seu aplicativo. Planeje tela de forma hierárquica e eficaz, pensando principalmente em usuário que possuem grandes experiências. Um bom exemplo disso é a utilização de abas laterias para a navegação.
    3-Não utilize muitas notificações: muita informação aparecendo para o usuário é um fruto para o desastre de sua app. Use notificações apenas para ajudar o usuário a entender o que está acontencedo dentro de seu app. Você deve prever um recurso em que o usuário possa desligar todas as notificações com um simples clique.
    4-Concentre na beleza de seus aplicativos: Este é um item que muitos não dão muita importância porém, é um dos itens muito importantes no planejamento de seu aplicativo. Faça a aparência do mesmo de forma a surpreender as expectativas do usuário. Faça uma boa utilização de cores, de forma com que elas se combinem entre si. Utilize uma boa combinação para texto, ícones entre outros elementos de seu aplicativo.
    Essas boas práticas de Comunicação visual são de certa forma o conteúdo, às informações essenciais que servem de base para um raciocínio, para um estudo que levará a uma excelente compreensão do seu aplicativo.

    Construa seu aplicativo em compreensão com o seu cliente, atendendo as necessidades reais do mesmo.

    Grande abraço a equipe do Tasafo, e a todos os leitores,
    Recomendo de mais este Blog.

  • Ramon Rabello

    Obrigado por compartilhar seu conhecimento conosco, Jonathan! E desde já convidamos você para escrever um post maneiro sobre essas dicas sobre Comunicação Visual, que tal? 🙂

  • Alexandre

    Impor o usuário a utilizar sua app de uma única maneira

    Só discordo desse ponto, se for um app pra realizar algum trabalho não tem como, nem todo app é pra social como o whatsapp.

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *