Boas práticas no desenvolvimento em Android

Veja neste artigo a qualidade do planejamento do projeto à entrega ao cliente para tornar suas aplicações mais eficientes e alcançar um grande número de downloads.

Fique por dentro

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:

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.

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.

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:

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;

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:

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:

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;

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:

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;

É 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;

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:

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:

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:

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

Artigos relacionados