Este artigo faz parte de um Guia de Desenvolvimento para Android. O guia fala de vários temas necessários para construir uma aplicação simples na sua totalidade, abordando várias funcionalidades introduzidas no Honeycomb e que ainda são válidas para o Ice Cream Sandwich. Usamos um cliente do Google Buzz chamado Honeybuzz como exemplo para cada tópico. Na introdução encontra-se uma listagem completa de todos os artigos.
O registo de erros e a forma como se lidam com estes é um aspecto importante duma aplicação, não só porque permite descobrir as áreas mais problemáticas de uma aplicação, mas também para fornecer uma boa experiência ao utilizador quando um erro ocorre.
Uma biblioteca popular para lidar com erros em Android é o ACRA. O ACRA fornece relatórios detalhados com informação de cada erro e guarda a informação num formulário Google Doc (ou envia a informação para um script custom ou até para um email - mas um Google Doc é prático e fácil de configurar).
Além disso, o ACRA é personalizável na forma como os erros são apresentados ao utilizador: tanto pode enviar erros silenciosamente sem chamar a atenção, como também é possível mostrar uma caixa de diálogo ao utilizador em que este tem de aceitar voluntariamente o envio do erro e pode ainda deixar uma descrição.
A partir do Froyo, os relatórios de crash são enviados para o programador através do Android Market (se o utilizador aceitar enviá-los), mas o ACRA é mais personalizável e fornece relatórios detalhados.
Configurar uma Google Spreadsheet para relatórios do ACRA
Podem ler um guia de instalação do ACRA no site do projecto. Vou descrever brevemente o procedimento.
- Fazer download da última versão a partir do site do ACRA.
- Vão ao Google Docs e importem o template CrashReports-template.csv. Certifiquem-se que a conversão está activa.
- Abram o novo documento e vão a Tools > Form > Create a form.
- Copiem o valor formKey que está visível na parte de baixo da janela.
- Escrevam algo na descrição para poderem gravar o formulário.
- Se quiserem receber notificações de cada vez que um relatório é gerado (ou diariamente por exemplo), podem editar as regras de notificação.
- No interface antigo do Google Docs expandam o Share e a opção aparece.
- No novo interface do Google Docs têm de ir a Tools > Notification rules...
- Para lerem as mensagens de erro com mais facilidade devem redimensionar as colunas e mudar o alinhamento desta forma:
Adicionar o ACRA ao vosso projecto
Adicionem a biblioteca ACRA ao vosso projecto. Sigam as instruções fornecidas no artigo Android 101: Ambiente de desenvolvimento e criação do projecto (ver a parte de como adicionar bibliotecas externas ao projecto).
Na configuração mais básica, só é necessário adicionar um atributo à classe Application com a chave do formulário da vossa Google Spreadsheet. Para esta aplicação, fomos um pouco mais longe e adicionamos as seguintes opções:
@ReportsCrashes(formKey="your form key",
mode = ReportingInteractionMode.NOTIFICATION,
resToastText = R.string.crash_toast_text, // optional, displayed as soon as the crash occurs, before collecting data which can take a few seconds
resNotifTickerText = R.string.crash_notif_ticker_text,
resNotifTitle = R.string.crash_notif_title,
resNotifText = R.string.crash_notif_text,
resNotifIcon = android.R.drawable.stat_notify_error, // optional. default is a warning sign
resDialogText = R.string.crash_dialog_text,
resDialogIcon = android.R.drawable.ic_dialog_info, //optional. default is a warning sign
resDialogTitle = R.string.crash_dialog_title, // optional. default is your application name
resDialogCommentPrompt = R.string.crash_dialog_comment_prompt, // optional. when defined, adds a user text field input with this text resource as a label
resDialogOkToast = R.string.crash_dialog_ok_toast, // optional. displays a Toast message when the user accepts to send a report.
sharedPreferencesName = HoneybuzzApplication.PREFERENCES_NAME, // Name of the SharedPreferences that will host the acra.enable or acra.disable preference.
sharedPreferencesMode = HoneybuzzApplication.PREFERENCES_MODE // The mode that you need for the SharedPreference file creation: Context.MODE_PRIVATE, Context.MODE_WORLD_READABLE or Context.MODE_WORLD_WRITEABLE.
)
public class HoneybuzzApplication extends Application {
public static final String PREFERENCES_NAME = "HoneybuzzPrefs";
public static final int PREFERENCES_MODE = MODE_PRIVATE;
@Override
public void onCreate() {
ACRA.init(this); // error reporting
Logging.setLoggingLevel(); // logging level
PreferenceManager.setDefaultValues(this, PREFERENCES_NAME, PREFERENCES_MODE, R.xml.preferences, false); // set default preferences
super.onCreate();
}
}
Especificamos o modo de interacção dos erros, umas mensagens e ícones personalizados e o nome das preferências para que estas possam ser gravas pelos utilizadores.
Existem três tipos de interação:
- SILENT: os erros são enviados sem notificar os utilizadores e é mostrado o ecrã padrão do Android (a partir do Froyo).
- TOAST: é mostrada ao utilizador uma simples mensagem durante alguns segundos, avisando que foi enviado um relatório de erros, sem que esta tenha que dar qualquer tipo de confirmação.
- NOTIFICATION: uma notificação é mostrada ao utilizador que este tem que aceitar para que o relatório de erros seja enviado, com a possibilidade de deixar informação adicional.
Outra funcionalidade interessante é que os utilizadores podem especificar nas preferências da aplicação como querem que seja feita a interacção dos erros.
Classe de Logging
Acabamos por criar uma classe à medida, semelhante à classe android.utils.Log, para agregar toda a funcionalidade de logging: não apenas para escrever relatórios ACRA, mas também para escrever para os logs do Android (que podem ser lidos atráves do LogCat). Esta classe é usada ao longo da aplicação para gravar mensagens, assim como excepções e erros.
Dependendo do nível de logging configurado, os relatórios seriam enviados para o ACRA.
package com.quasibit.honeybuzz;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.acra.ErrorReporter;
import android.util.Log;
public class Logging {
private static Level LOGGING_LEVEL = Level.ALL;
private static final String TAG = "Honeybuzz";
public static void setLoggingLevel() {
Logger.getLogger("com.google.api.client").setLevel(LOGGING_LEVEL);
}
public static int e(String tag, String msg, Throwable tr) {
acraReport(Level.SEVERE, tag, msg, tr);
return Log.e(tag, msg, tr);
}
public static int e(String msg, Throwable tr) {
return e(TAG, msg, tr);
}
public static int e(Throwable tr) {
return e(TAG, tr.getMessage(), tr);
}
public static int w(String tag, String msg, Throwable tr) {
acraReport(Level.WARNING, tag, msg, tr);
return Log.w(tag, msg, tr);
}
public static int w(String msg, Throwable tr) {
return w(TAG, msg, tr);
}
public static int w(String tag, String msg) {
acraReport(Level.WARNING, tag, msg);
return Log.w(tag, msg);
}
public static int w(String msg) {
return w(TAG, msg);
}
public static int i(String tag, String msg, Throwable tr) {
acraReport(Level.INFO, tag, msg, tr);
return Log.i(tag, msg, tr);
}
public static int i(String msg, Throwable tr) {
return i(TAG, msg, tr);
}
public static int i(String tag, String msg) {
acraReport(Level.INFO, tag, msg);
return Log.i(tag, msg);
}
public static int i(String msg) {
return i(TAG, msg);
}
public static int d(String tag, String msg) {
acraReport(Level.FINEST, tag, msg);
return Log.d(tag, msg);
}
public static int d(String msg) {
return d(TAG, msg);
}
public static int v(String tag, String msg, Throwable tr) {
acraReport(Level.FINE, tag, msg, tr);
return Log.v(tag, msg, tr);
}
public static int v(String msg, Throwable tr) {
return v(TAG, msg, tr);
}
public static int v(String tag, String msg) {
acraReport(Level.FINE, tag, msg);
return Log.v(tag, msg);
}
public static int v(String msg) {
return v(TAG, msg);
}
private static void acraReport(Level level, String tag, String msg, Throwable tr) {
if (level.intValue() >= LOGGING_LEVEL.intValue()) {
acraReport(level, tag, msg);
// report exception to acra silently
ErrorReporter.getInstance().handleSilentException(tr);
}
}
private static void acraReport(Level level, String tag, String msg) {
if (level.intValue() >= LOGGING_LEVEL.intValue()) {
ErrorReporter.getInstance().putCustomData(level.getName() + ": " + tag, msg);
}
}
}
Análise de relatórios ACRA
O Google Spreadsheets não fornece propriamente uma experiência agradável para a leitura de relatórios de erros. Existem aplicações que foram feitas para lidar com os relatórios criados pelo ACRA. Podem ver alguns exemplos na página de contribuições no site do ACRA.
O BugSense, por exemplo, funciona bem com o ACRA. Ainda não o testei, mas parece ter um bom conjunto de funcionalidades e apenas requer uma configuração mínima.