Por que eu devo ler este artigo:

Este artigo será útil para a criação de bons hábitos no desenvolvimento de aplicações Android, explicará a importância de controlar acessos externos inseridos na sua aplicação (como mapas e conexões de internet) e mostrará ferramentas para testar seu código.

Ajudará também a descobrir o que deve ser evitado desde a codificação do aplicativo até a finalização e entrega, para garantir uma boa performance final do produto e a satisfação do usuário.

Atualmente o smartphone se tornou parte essencial do dia de milhares de pessoas no mundo inteiro, a venda desses aparelhos não para de crescer e sua abrangência de utilidades continua nos surpreendendo. Um conjunto de novas ferramentas e melhorias são entregues todo ano pelas grandes marcas, garantindo nosso contínuo interesse na utilização dos aparelhos e aguçando nossa curiosidade com relação aos limites de cada dispositivo.

Com isso, o mercado de aplicativos também apresenta crescimento, tanto na procura de produtos que utilizem de formas novas e criativas as ferramentas do dispositivo quanto em lançamentos de produtos destinados para os mais diversos fins.

Mas para acompanhar a demanda, muito tem se perdido em boa execução da ferramenta e qualidade de experiência entregue ao usuário.

Para evitar o descuido no desenvolvimento do seu aplicativo, vamos mostrar alguns pontos importantes a serem praticados durante a sua idealização, verificações que devemos prever durante a codificação e os detalhes finais para otimizar a entrega do produto final ao usuário. Além disso, também é preciso passar pelos pontos que devemos evitar, como falta de compatibilidade entre aparelhos e erros comuns que cometemos na ansiedade da finalização. Demonstraremos isso utilizando a plataforma Android como base, mas os conceitos aqui abordados podem ser empregados para todas as plataformas mobile.

Seu consumidor

É importante lembrarmos que o usuário da nossa aplicação é nosso consumidor. Ele tratará nosso aplicativo como um produto, analisará ele como tal e se não estiver satisfeito, irá descartá-lo e comprar um novo e melhor produto. Garantir que ele tenha a melhor experiência possível com seu aplicativo é parte importante da utilização e aceitação da sua proposta.

Para isso, precisamos mais que imagens e telas bonitas, precisamos que a aplicação pense em seu consumidor final e que sua idealização reflita a necessidade que o mesmo procura sanar com seu produto.

Ao criar a parte visual do seu aplicativo, pense em como o usuário utilizará durante o dia, tente prever situações comuns como navegar no aplicativo em movimento, ter pouco tempo para chegar até a função que ele deseja usar naquele momento, para qual tipo de usuário o aplicativo realmente será útil, entre outras questões.

Apesar do fácil acesso a informações e manuais e do alto índice de vendas de dispositivos touch que encontramos hoje, é preciso pensar nos usuários que não estão acostumados com toda essa tecnologia.

Pessoas com mais idade ou com baixa renda que nunca tiveram contato com um dispositivo que possibilita acesso a um mundo pelo toque na tela podem precisar de uma ajuda visual mais completa e uma interface bem mais intuitiva que o normal.

A correlação com o mundo real é um forte aliado na construção visual do seu aplicativo, possibilitando o fácil entendimento através de palavras, frases e conceitos familiares do dia a dia.

Prever o uso do dispositivo por crianças também é importante, principalmente pelo uso comum de tablets para entretê-las com aplicativos infantis ou desenhos. Caso seu aplicativo possua conteúdo adulto, não deixe imagens ou vídeos explícitos dispostos nas primeiras telas da aplicação e mantenha um controle de idade para acessar o conteúdo final. O usuário se sentirá mais seguro ao baixar sua aplicação e a frequência de uso será maior.

Interface otimizada

A interface é sua primeira impressão com o consumidor. Através dela você pode chamar a atenção de um público específico ou de uma massa, pode garantir o uso contínuo da sua aplicação e a satisfação do usuário. Uma interface bem elaborada e otimizada deve contemplar os seguintes itens:

  • Fácil navegação: utilizar sempre ícones e imagens intuitivas, que descrevam sem dificuldade sua função no app. Evite descrições ou legendas muito grandes em menus e abas;
  • Bons textos: manter descrições e mensagens objetivas e claras, com tempo e visualização confortáveis para o usuário;
  • Linguagem: se o seu aplicativo possui versões em outras línguas, é preciso ficar sempre atento às traduções. O não entendimento de uma instrução ou função pode levar o usuário a desinstalar o aplicativo ou negativar seu aplicativo na Google Play Store, o que dificulta a aceitação de outros usuários;
  • Tutoriais: evite sempre que possível a necessidade de auxílio para utilização e navegação do seu aplicativo, mas caso haja uma funcionalidade muito complexa ou com muitas etapas, crie um tutorial que utilize a interface visual criada como base, para que o usuário encontre suporte. Deixe o tutorial sempre à disposição em um local de fácil acesso, para que o usuário acesse sempre que tiver alguma dúvida;
  • Consistência: O usuário não deve se perguntar se diferentes imagens ou descrições significam a mesma coisa. Se na tela principal do seu aplicativo você tem um botão com o título Mapa, abra a tela e mantenha o título Mapa. Não dificulte a navegação e não deixe seu usuário confuso;
  • Eficiência de uso: dispor de atalhos para tarefas, um conjunto de ferramentas de fácil acesso em um local da aplicação ou mesmo o uso de máscaras e navegação sequencial em campos de texto beneficia o aproveitamento pelo usuário e otimiza o tempo de uso. Procure ajudar o usuário desde o primeiro acesso a explorar a aplicação de forma completa;
  • Layout otimizado: quando tratamos de dispositivos Android, temos as diferentes resoluções e tamanhos como um problema crítico e facilmente encontrado em diversos aplicativos. Criar um layout dinâmico muitas vezes resolve, mas quando temos muitas imagens, precisamos utilizar diferentes resoluções. Para isso, a estrutura inicial do seu projeto Android disponibiliza pastas próprias para cada tipo de resolução, como mostra a Figura 1.
Pastas drawable diferenciadas pela qualidade da resolução
Figura 1. Pastas drawable diferenciadas pela qualidade da resolução

Caso as pastas padrão não comportem sua resolução, é possível criar uma pasta específica para o modelo de smartphone Android com densidades e resoluções fora dos padrões comuns.

Com relação a Tablets versus Smartphones, não devemos nos prender a simplesmente adaptar nosso layout para uma tela maior ou com resolução alta. Usar os diferentes dispositivos para criar uma interface diferenciada é uma boa opção para aproveitar melhor o espaço ou até inserir uma funcionalidade diferente. Assim como as pastas de imagens, também é possível gerar uma pasta específica para cada tipo de aparelho. Para isso, crie uma pasta com o nome layout-land, copie o arquivo XML que deseja otimizar para a versão tablet e o coloque dentro da nova pasta, como mostra a Figura 2.

Pastas de layout diferenciadas pelo tipo de dispositivo
Figura 2. Pastas de layout diferenciadas pelo tipo de dispositivo

Uma vez criada as duas versões do arquivo XML, altere o código dos arquivos de acordo com a necessidade para cada tipo de dispositivo e o próprio sistema Android localizará o layout que melhor corresponde ao aparelho que está executando o programa.

Ciclo de vida do aplicativo

As classes codificadas em um programa Android possuem um ciclo de vida contendo os seguintes estágios como principais: Iniciada, Em uso, Pausada, Interrompida e Destruída. Este ciclo deve ser inserido em cada classe criada pelo desenvolvedor, respeitando a ordem de cada estágio, pois uma vez não implementado corretamente pode causar o fechamento inesperado da aplicação.

Durante o desenvolvimento encontramos diversos cenários onde o não tratamento dos dados dentro do ciclo de vida da aplicação pode ocasionar erros críticos ou causar um comportamento não esperado. Como exemplos podemos citar a ação do usuário de atender uma ligação durante o uso do programa e voltar para o mesmo após desligar, o carregamento de um conteúdo externo através de um link que abra um navegador, ou até mesmo uma função que chame a agenda ou o calendário do aparelho.

Todos esses e outros casos precisam ser tratados para garantir que o usuário continue de onde parou, sem perder nenhum dado ou sem que o aplicativo feche sem motivos aparentes.

Mesmo após um fechamento inesperado, quando seu aplicativo for reaberto, ele precisa ter salvo as informações inseridas pelo usuário e continuar seu funcionamento de forma estável.

É necessário também respeitar a hierarquia dos métodos dentro do ciclo de vida da activity. Se atribuirmos um valor em um TextView no método onCreate() e no onResume() atribuirmos outro valor a esse mesmo TextView, ao pausar a Activity e voltar para ela, apenas o valor atribuído no onResume() será mostrado, pois o método onCreate() só é chamado quando a classe é instanciada pelo sistema, e o método onResume() é chamado sempre que a classe entra em foco novamente, não tendo sido finalizada. Portanto, observe atentamente a ordem dos métodos contidos no ciclo, conforme mostra a Figura 3, e busque trabalhar em cima das condições exigidas para manter integra a execução das tarefas desejadas.

Ciclo de vida de uma aplicação Android
Figura 3. Ciclo de vida de uma aplicação Android

Conectividade

Existem raros casos em que um aplicativo não precisa, em nenhum momento, de uma conexão com a internet. Caso seu código exija conexão em alguma ocasião, é indispensável garantir que ele consiga utilizar as funções de forma correta e sem erros, algo que pode ser fácil se lembrarmos de alguns pontos:

  • Operações em Background: as operações que utilizam internet devem sempre ser executadas separadas da UIThread. Dessa forma, o usuário não tem a aplicação travada durante a comunicação de dados;
  • Status da comunicação: sempre que uma operação que utilize a internet for necessária, tenha como costume inserir um diálogo alertando o usuário sobre o que está acontecendo e, se possível, o status da ação;
  • Tipos de conexão: se o seu aplicativo possui a necessidade de conexão com a internet, ele precisa estar preparado para responder aos diferentes tipos de conexão. Aqui no Brasil, temos o uso de conexão 3G como principal, seguida por WiFi e pacotes de dados oferecidos por operadoras.

Criar mecanismos para diferentes velocidades de conexão é necessário para que o usuário não fique preso por muito tempo a uma operação ou até para garantir que ele sempre tenha alguma resposta;

  • Estabilidade: tratar uma queda de rede durante a execução de um método que utiliza conexão é imprescindível para a utilização do aplicativo. Sempre que ocorrer, mostre um alerta ao usuário e possibilite que ele repita o processo sem que perca nenhum dado;
  • Segurança: muitas vezes, por julgarmos seguro o endereço ao qual enviamos informações coletadas no aplicativo, abrimos mão de uma garantia de segurança criada antes do envio. Sendo assim, criptografar dados ao enviá-los para algum serviço e não salvar senhas na aplicação são procedimentos básicos para a segurança do usuário.

Utilização de GPS

Atualmente, muitas aplicações possuem funções e estatísticas se baseando na localização do usuário. Para mostrar um mapa, procurar lugares próximos, mostrar o endereço do usuário ou traçar uma rota, ativamos o GPS na aplicação. Mas devemos também nos lembrar de desativá-lo quando não estivermos utilizando. A atualização constante da localização do usuário recorre ao serviço de GPS do aparelho ininterruptamente, causando o alto consumo de memória e bateria.

A criação de uma classe responsável pelo gerenciamento do GPS é uma grande aliada para garantir a correta implementação da respectiva função no aplicativo. Na Listagem 1 podemos observar um exemplo desse tipo de classe.

public class GPSTracker extends Service implements LocationListener {
 
    private final Context mContext;
 
    // flag para status do GPS
    boolean isGPSEnabled = false;
 
    // flag para status da Internet
    boolean isNetworkEnabled = false;
 
    // flag para localização GPS
    boolean canGetLocation = false;
 
    Location location; // localização
    double latitude; // latitude
    double longitude; // longitude
 
    // Distância mínima em metros para atualizar a localização
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; 
    // 10 metros
 
    // Tempo mínimo em milissegundos entre atualizações
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1;    
    // 1 minuto
 
    // Declaração do Gerenciador de Localização
    protected LocationManager locationManager;
 
    public GPSTracker(Context context) {
        this.mContext = context;
        getLocation();
    }
}
Listagem 1. Código da classe GPSTracker

Após criar a classe e adicionar as variáveis globais e o seu construtor, precisaremos criar a função responsável por obter a localização do usuário. Sendo assim, adicione à sua classe GPSTracker o método apresentado na Listagem 2. Este verificará se existe alguma conexão ou se o GPS do aparelho está ativo para a procura da localização, e caso seja possível realizar a busca, ela chamará os métodos que darão continuidade no processo.

public Location getLocation() {
  try {
    locationManager = (LocationManager) mContext
      .getSystemService(LOCATION_SERVICE);
 
    // obtendo o status do GPS
    isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
 
    // obtendo o status da Internet
    isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
 
    if (!isGPSEnabled && !isNetworkEnabled) {
      // nenhuma rede está ativa
    } else {
      this.canGetLocation = true;
      // Primeiro carregue a localização pela rede
      if (isNetworkEnabled) {
        locationManager.requestLocationUpdates(
          LocationManager.NETWORK_PROVIDER,
          MIN_TIME_BW_UPDATES,
          MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
        Log.d("Network", "Network");
        if (locationManager != null) {
          location = locationManager
            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
          if (location != null) {
            latitude = location.getLatitude();
            longitude = location.getLongitude();
          }
        }
      }
      // se o GPS está Ativo, carregue lat/long usando os serviços de GPS
      if (isGPSEnabled) {
        if (location == null) {
          locationManager.requestLocationUpdates(
            LocationManager.GPS_PROVIDER,
            MIN_TIME_BW_UPDATES,
            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
          Log.d("GPS Ativo", "GPS Ativo");
          if (locationManager != null) {
            location = locationManager
              .getLastKnownLocation(LocationManager.GPS_PROVIDER);
            if (location != null) {
              latitude = location.getLatitude();
              longitude = location.getLongitude();
            }
          }
        }
      }
    }
  } catch (Exception e) {
    e.printStackTrace();
  }
  return location;
}
Listagem 2. Método responsável pela localização

Implementado o método responsável por obter a localização do usuário, precisaremos dos métodos responsáveis por obter a latitude e longitude com base na localização recebida, expostos na Listagem 3. Estes verificam se o valor da localização é diferente de nulo, e caso seja, retornam os valores de latitude e longitude contidos dela.

//Método para buscar a latitude
public double getLatitude(){
    if(location != null){
        latitude = location.getLatitude();
    }
    return latitude;
}
     
//Método para buscar a longitude
public double getLongitude(){
    if(location != null){
        longitude = location.getLongitude();
    }
    return longitude;
}
Listagem 3. Métodos para obter latitude e longitude

Para garantir a estabilidade na nossa aplicação, devemos verificar se o GPS/Internet está ativado no aparelho do usuário conforme mostra a Listagem 4, antes de tentar obter alguma coordenada. Caso não seja possível captar a localização, devemos alertar o usuário e solicitar que o mesmo ative as funções em seu aparelho para que ele possa fazer uso do aplicativo da forma correta.

/**
 * Método para checar se o GPS/Internet está ativo
 * @return boolean
 * */
public boolean canGetLocation() {
    return this.canGetLocation;
}
     
/**
 * Função para mostrar alerta para Configurações
 * Pressionando o botão Configurações abrirá as configurações do seu aparelho
 * */
public void showSettingsAlert(){
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
      
    // Configurando título do Dialog
    alertDialog.setTitle(“Configuração do GPS");
  
    // Configurando mensagem do Dialog
    alertDialog.setMessage("GPS não está ativo. Deseja ir para as configurações do aparelho?");
  
    // Pressionando o botão Configurações
    alertDialog.setPositiveButton("Configurações", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,int which) {
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            mContext.startActivity(intent);
        }
    });
  
    // Pressionando o botão Cancelar
    alertDialog.setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
        dialog.cancel();
        }
    });
  
    // Mostrando o alerta
    alertDialog.show();
}
Listagem 4. Métodos para verificar se é possível obter localização

Para finalizar nossa classe, devemos criar um método responsável por desligar o GPS quando não formos mais utilizá-lo, para evitar o alto consumo de bateria e memória sem necessidade, o que é feito na Listagem 5.

public void stopUsingGPS(){
    if(locationManager != null){
        locationManager.removeUpdates(GPSTracker.this);
    }       
}
Listagem 5. Método para desligar o GPS

Uma vez implementada a classe com todos os elementos de gerenciamento, alertas e verificações, podemos adotá-la sempre que for necessário em uma Activity recuperar informações sobre a localização do usuário. Um exemplo disso é exposto na Listagem 6.

public class MeuAplicativoActivity extends Activity {
     
  Button btnShowLocation;
   
  GPSTracker gps;
   
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
     
    btnShowLocation = (Button) findViewById(R.id.btnShowLocation);
     
    // mostra localização ao clicar no botão
    btnShowLocation.setOnClickListener(new View.OnClickListener() {
         
        @Override
        public void onClick(View arg0) {        
          // cria um objeto da classe
          gps = new GPSTracker(AndroidGPSTrackingActivity.this);

          // checa se o GPS está ativo     
          if(gps.canGetLocation()){
               
              double latitude = gps.getLatitude();
              double longitude = gps.getLongitude();
               
              // \n é para uma nova linha
              Toast.makeText(getApplicationContext(), “Sua localização é - \nLat: " + 
               latitude + "\nLong: " 
               + longitude, Toast.LENGTH_LONG).show();    
          }else{
            // não é possível obter a localização
            // GPS ou Internet não está ativo
            // Pede para o usuário ativar o GPS/Internet nas configurações
            gps.showSettingsAlert();
          } 
        }
      });
  }
}
Listagem 6. Exemplo de uso da classe de gerenciamento do GPS em uma Activity

Armazenamento em banco

Com o fácil acesso a redes de internet, a grande maioria dos aplicativos atuais não necessita de um banco de dados interno. Mas se o volume de informações que você precisa carregar é muito grande, a criação de um banco SQLite (Nota 1) dentro da sua aplicação é a melhor opção para agilizar os processos. Caso opte por implementar um banco de dados interno, mantenha-se atento a alguns cuidados que devem ser tomados durante a codificação, tais como:

  • Criação das tabelas: caso seu banco de dados vá ser populado por dados recebidos por um serviço, procure manter a nomenclatura dos campos e os tipos corretos na hora de criar sua tabela. Muitos erros são ocasionados por conversões erradas ou atribuições diferentes das esperadas;
  • Utilização: lembre-se sempre de abrir o banco de dados antes de utilizá-lo e de fechá-lo após o uso. Esquecer esses dois passos simples pode ocasionar em um erro crítico, que vai fechar inesperadamente sua aplicação;
  • Onde salvar o banco: se sua aplicação contém informações sigilosas e que não devem ser acessadas fora da aplicação, crie seu banco de dados em uma pasta interna do aplicativo e não em uma pasta no SDCard. Além disso, se possível, crie uma senha para a pasta. As pastas do SDCard são facilmente acessadas por um computador e suas informações podem vazar;
  • Acessos concorrentes: tenha cuidado ao iniciar uma rotina no banco de dados. Caso você esteja fazendo um update com um grande volume em uma tabela e tentar fazer um select na mesma, a aplicação travará;
  • Tratamentos de valores nulos: é preciso sempre tratar valores nulos. Caso você faça um select, o retorno for nulo e logo em seguida você tente utilizar o valor, seu aplicativo fechará inesperadamente. Portanto, verifique sempre se o retorno do banco é diferente de nulo antes de usá-lo para alguma ação;
  • Gerenciamento: da mesma forma que o GPS, a melhor forma de trabalhar com um banco de dados é criando uma classe responsável pelo gerenciamento das funções. Centralizar acessos dentro de todo o seu aplicativo evita erros por esquecimento ou distração.
Nota 1: SQLite — É um banco de dados mais compacto, com suporte nativo no Android. Sua criação é muito simples e não há necessidade de configurações ou instalação no aparelho antes da utilização do mesmo em um aplicativo.

Manipulando mídias

Trabalhar com mídias dentro da aplicação requer muitos cuidados, porém executá-las de maneira limpa e que garanta o funcionamento sem interrupções ou travamentos pode ser uma tarefa fácil quando nos atentamos a alguns itens, tais como:

  • Memória consumida: a utilização de imagens e vídeos costuma consumir muita memória do aparelho. Sendo assim, certifique-se de tratar exceções de estouro de memória, mostrando o ocorrido ao usuário e retomando a ação sem maiores danos à sua aplicação;
  • Respeitar o volume: sempre que for reproduzir um áudio ou vídeo, respeite o controle de volume do aparelho, não tente forçar um volume por código. Ainda que o alerta sonoro ou o áudio do vídeo sejam importantes na aplicação, o usuário deve definir a intensidade do volume;
  • Salvar configurações: se a reprodução de um vídeo ou áudio foi interrompida pelo usuário, seja pausada, ou caso o aplicativo entre em segundo plano, é necessário tratar a interrupção e salvar o estado atual da reprodução.

Utilizar o ciclo de vida da aplicação te ajudará nessa tarefa, pois atender uma ligação durante a reprodução de um vídeo na sua aplicação e ainda ouvir o áudio enquanto tenta conversar com alguém é algo realmente incômodo e que pode acarretar na desinstalação do aplicativo ou desuso da função.

Também é importante que, nesse mesmo cenário apresentado, o usuário consiga continuar a reprodução do ponto em que parou, e não tenha que voltar tudo ou reiniciar a reprodução;

  • Status de reprodução: é importante que o usuário saiba o que está acontecendo com a reprodução, se o arquivo ainda está sendo carregado, qual a duração total dele e em qual ponto de reprodução ele se encontra. Isso auxilia o usuário a decidir se reproduzirá agora ou se tem pouco tempo para iniciar a reprodução do arquivo;
  • Orientação do aparelho: quando reproduzimos vídeos, é comum que o usuário vire o aparelho para a posição paisagem para ter uma visualização maior e mais detalhada. Travar a rotação de tela através da classe que reproduzirá o vídeo pode ser uma frustração para o usuário;
  • Galerias: ao apresentar uma galeria de imagens ou vídeos no aplicativo, é preciso ter alguns cuidados. Assim, procure fazer cache das imagens que aparecerão na galeria, e se possível, tenha imagens com baixas resoluções próprias para as miniaturas que geralmente são apresentadas como prévia do arquivo.

Mensagens e chamadas

Enviar mensagens e realizar chamadas por código dentro da aplicação não é complicado, mas é preciso prever algumas situações para não ocasionar erros ou dúvidas, saber a melhor opção de envio para cada necessidade e analisar se a implementação enriquecerão uma função do programa ou se só substituirá a interface padrão disponível nos aparelhos. Relacionado a essa questão, os principais aspectos que devemos nos atentar são:

  • Envios de SMS/MMS: quando o envio de um SMS/MMS for feito diretamente de dentro do código do app, sem utilizar o aplicativo nativo do Android, é importante garantir que a mensagem tenha sido enviada e, caso não tenha sido possível, alertar o usuário sobre a falha;
  • Caracteres especiais: quando enviamos mensagens diretamente por código, precisamos verificar se a String contém caracteres especiais como acentos e pontuações para tratá-los e evitar que o aplicativo interprete de maneira errada, caso contrário o usuário enviará símbolos ou caracteres estranhos acreditando que a mensagem foi recebida da forma correta.

Para evitarmos esse tipo de erro existem dois cenários que devemos prever. Um deles está relacionado a quando o texto a ser enviado é inserido pelo usuário.

Nestes casos, verifique o texto antes do envio, como mostra a Listagem 7. Neste código, recuperamos o texto definido em um EditText e aplicamos UTF-8, que será o responsável por assegurar que os caracteres especiais serão enviados. O outro cenário ocorre quando a mensagem a ser enviada é definida pelo sistema.

Nestes casos, carregue a mensagem a partir do strings.xml, arquivo criado no escopo inicial do projeto que é responsável por centralizar as Strings a serem utilizadas na aplicação e que já possui uma configuração que mantém os caracteres especiais;

  • Permitir chamadas: muitas vezes encontramos meios de não interromper uma execução importante para a aplicação impedindo algumas ações no dispositivo do celular.

É possível, por código, impedir que o aparelho receba alguma chamada durante a execução de um método, mas é altamente perigoso. Não devemos bloquear nenhuma função nativa do aparelho do usuário, e garantir que ele possa receber chamadas, principalmente de emergência, é de suma importância;

  • Edição do número: se a ligação ou envio de SMS for feito diretamente por código, permita que o usuário insira, procure na agenda ou edite um número que já está fixo na aplicação. Ademais, o usuário pode estar em outro estado ou país e deve poder optar por qual operadora utilizar antes de finalizar a ação.
String msgVerificada = Charset.forName(“UTF-8”)
.encode(editTextMensagem.getText().toString());
Listagem 7. Código para verificar caracteres especiais

Alertas

Mostrar o status das ações aos usuários é imprescindível, mas é preciso evitar excessos e utilizar os alertas de forma inteligente para que a mensagem não seja perdida ou má interpretada e para que a fluidez do uso seja mantida. Dentre os pontos relacionados que devemos ser cautelosos, podemos citar:

  • Diagnósticos: as mensagens de erro devem indicar o problema e ajudar o usuário a solucioná-lo. Mensagens com instruções muito grandes ou descrições utilizando códigos de erros devem ser evitadas;
  • Prevenção: sempre que uma ação tomada pelo usuário cause uma alteração muito brusca, informe ele sobre isso e pergunte se é o que ele realmente deseja fazer naquele momento. Seja breve em descrever a ação e o resultado, caso ele deseje prosseguir, e permita que ele cancele a ação;
  • Mantenha o simples: o sistema Android possui um padrão de alertas. Tente não fugir do padrão para que o usuário não se sinta confuso e para que sua aplicação não fique carregada com layouts customizados para mostrar apenas uma mensagem;
  • Alertas de progresso: se a aplicação mostrará um status para o usuário, se questione se realmente é necessário travar a aplicação e aguardar o término do progresso ou se ele pode continuar a navegar.

Caso ele possa navegar e a mensagem de status não seja muito relevante, prefira mostrar um Toast (uma pequena janela de mensagem que aparece na parte inferior da tela durante um tempo) no lugar de um alerta. Caso a aplicação precise que a função termine para continuar com o funcionamento correto, defina por código que o usuário não poderá cancelar o alerta, como mostra a Listagem 8.

ProgressDialog dialog = new ProgressDialog(this);
dialog.setMessage("carregando...");
dialog.show();
dialog.setCancelable(false);
dialog.setCanceledOnTouchOutside(false);
Listagem 8. Exemplo de alerta de progresso

Testar nunca é demais

Os testes em uma aplicação devem abranger o máximo de itens que compõem o projeto, desde os testes de usabilidade do produto, testes de código e tempo de vida da atividade, até os testes para funcionalidades específicas. Testar todas essas frentes é essencial, pois ainda que o funcionamento geral esteja conforme, se as atividades não tiverem uma boa resposta, ou se o layout não for intuitivo e de fácil utilização, de nada adiantará uma ótima ideia escondida na aplicação. Para garantir testes mais completos, dispomos de várias ferramentas que podem auxiliar nessa tarefa. A seguir são listadas algumas delas:

  • MonkeyRunner: fornece uma API para a criação de códigos para testes, voltados principalmente para testes funcionais. MonkeyRunner disponibiliza alguns recursos exclusivos para testes no Android, tais como controle de vários dispositivos, teste funcional, teste de regressão e possibilita ainda que o desenvolvedor adicione classes próprias na API para testes específicos;
  • Robotium: é um framework de automação para testes em Android que possui suporte completo para aplicações nativas e híbridas. Permite testes mais robustos de interface do usuário utilizando o método caixa-preta (Nota 2);
  • MonkeyTalk: proporciona automatização de testes reais e funcionais utilizando checagens simples ou mais sofisticadas, através de classes codificadas com verificações específicas, baseadas em dados inseridos para simulação. Permite testes em aplicativos nativos e híbridos, executados em dispositivos reais ou simuladores.

Além do uso de ferramentas de teste, busque testar sua aplicação em uma versão do Android que não tenha modificações feitas por operadoras ou fabricantes. Isso ajudará a reconhecer possíveis erros gerados por diferentes versões de sistema e assegurará um bom funcionamento da aplicação em diferentes aparelhos.

Nota 2: Testes do tipo caixa-preta — Consistem em explorar as funcionalidades do sistema do ponto de vista do usuário, simulando um uso real. Geralmente é realizado por terceiros, mas também pode ser utilizado para validações pontuais dos desenvolvedores.

Entregando seu produto

Garantir que seu produto esteja funcionando de forma eficaz é muito importante, entregá-lo ao seu cliente apropriadamente é crucial. A primeira impressão deve ser uma experiência excepcional e qualquer ruído até a execução do aplicativo pode ser decisivo para o uso ou descarte do mesmo. Com isso em mente, quando gerar o executável final para enviar para a Google Play Store, faça uma última verificação nos seguintes itens:

  • Chaves das APIs: assegure que as chaves das APIs implementadas no aplicativo estão corretas, para garantir a funcionalidade e não causar transtornos ao usuário. Facebook, Google Maps e Twitter precisam de uma chave única que deve ser gerada no computador que compilará a versão final do seu aplicativo;
  • Limpar massa de testes: se sua aplicação dispõe de alguma base de dados para preencher listas ou carregar campos, ou contenha algum código específico para gerar a massa de testes, garanta que na versão final do executável esses códigos não estejam ativos e que a base esteja limpa. Informações desconexas na aplicação pode confundir o usuário e até causar erros durante a execução;
  • Somente permissões válidas: verifique sempre antes de gerar o executável final se a sua aplicação está realmente usando todas as permissões solicitadas no seu AndroidManifest. Permissões demais podem assustar o usuário e fazê-lo questionar a integridade do seu aplicativo;
  • Instalação no SDCard: se sua aplicação possui mais de 10mb para ser baixada e instalada do Google Play Store, permita que o usuário possa escolher onde prefere salvar a aplicação. Em aparelhos mais simples, o uso do SDCard é decisivo para o bom funcionamento de algumas funções que precisam de mais memória;
  • Prints para a loja: procure tirar prints utilizando a última versão do seu aplicativo. Escolha telas que consigam explicar ao usuário as principais funções que seu aplicativo apresenta. Evite prints com informações pessoais ou sigilosas, como códigos de acesso e nomes de clientes.

Procure padronizar seu código para que fique mais fácil localizar os pontos de falhas, estude códigos de outros desenvolvedores para aprimorar sua experiência e o seu conhecimento. Além disso, tenha em mente que o aplicativo do seu concorrente é o seu primeiro protótipo, e procure ver onde ele falha para que o seu possa ser superior desde o início.

Mantendo a atenção aos pontos levantados e uma rotina de verificações contínuas durante o processo de desenvolvimento, sua aplicação Android pode ser muito mais eficiente e alcançar um grande número de downloads e boas críticas na Google Play Store.

Confira também