Con la release 10.1 del Flash Player, Adobe ha introdotto una funzionalità importante per gli sviluppatori di Rich Internet Applications in Flex. La novità riguarda la possibilità di gestire le eccezioni non gestite ed evitare, quindi, la visualizzazione dei fastidiosissimi popup di errore o, in casi estremi, il blocco totale dell'applicazione.
Configurare il progetto
Per utilizzare la funzionalità di catch globale delle eccezioni è necessario compilare le nostre applicazioni per essere eseguite con il flash player 10.1 (o AIR 2.0).
Creiamo il progetto nel Flash Builder (o in alternativa Eclipse con installato il plugin Adobe Flash Builder 4), selezionando l'SDK Flex 4.1 per la compilazione, come mostrato in figura 1:
Dopo aver creato il progetto, dobbiamo forzare l'utilizzo del Flash Player 10.1, altrimenti, quando utilizzeremo la funzionalità di catch globale, l'editor segnalerà un errore in fase di compilazione e non sarà possibile lanciare la nostra applicazione. Per fare ciò, dalle preferenze del progetto selezioniamo la voce “Flex Compiler” e impostiamo la versione del player come mostrato nella figura seguente:
A questo punto siamo pronti per utilizzare il Global Exception Handler!
Un primo esempio di utilizzo del Global Handler
Prima di implementare l'handler globale, creiamo una semplice applicazione che, al click di un pulsante, genera un'eccezione che non viene gestita con un blocco try/catch.
Di seguito è riportato il codice dell'applicazione:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="250" height="200"
creationComplete="initApplication(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
/**
* Invocato al caricamento dell'applicazione
* Aggiunge il listener al click del pulsante
*/
private function initApplication(event:FlexEvent) : void {
buttonError.addEventListener(MouseEvent.CLICK, onClickButtonError);
}
/**
* Invocato quando si clicca sul pulsante buttonError
* Genera un'eccezione
*/
private function onClickButtonError(event:MouseEvent) : void {
throw new Error("ECCEZIONE NON GESTITA");
}
]]>
</fx:Script>
<s:Button x="50" y="70" label="Genera un errore!" width="150" height="60" id="buttonError"/>
</s:Application>
Quando viene cliccato il pulsante con label “Genera un errore!”, viene lanciata un'eccezione non gestita e verrà mostrato il popup con lo stacktrace dell'errore come mostrato nella figura seguente:
Per gestire le situazioni di errore simili a questa che abbiamo appena creato, Adobe ha rilasciato, con il flash player 10.1 e Air 2.0, il “global exception handler” che è un gestore supremo delle eccezioni. Per rendere l'idea, è come se l'intera applicazione fosse inglobata in un gigantesco blocco try/catch. Per impostare il gestore delle eccezioni è necessario eseguire il seguente codice al verificarsi dell'evento applicationComplete
di Flex:
loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorManager);
Quando un'eccezione non viene gestita verrà, quindi, invocato il metodo uncaughtErrorManager
.
Da notare il fatto che il gestore delle eccezioni deve essere impostato quando si verifica l'evento applicationComplete
e non creationComplete
.
CreationComplete
è lanciato quando tutti i componenti sono stati creati e resi visibili mentre ApplicationComplete
è lanciato quando tutti i componenti sono stati aggiunti alla DisplayList
ed è l'ultimo evento di cui viene effettuato il dispatch durante il processo di creazione di un'applicazione.
All'interno del metodo uncaughtErrorManager
dobbiamo inserire il codice che gestisce la condizione di errore globale come mostrato nel codice completo dell'applicazione di seguito riportato:
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="250" height="200"
creationComplete="initApplication(event)"
applicationComplete="applicationReady(event)">
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<fx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
import mx.controls.Alert;
import mx.events.FlexEvent;
/**
* Invocato al caricamento dell'applicazione
* Aggiunge il listener al click del pulsante
*/
private function initApplication(event:FlexEvent) : void {
buttonError.addEventListener(MouseEvent.CLICK, onClickButtonError);
}
/**
* Invocato quando si clicca sul pulsante buttonError
* Genera un'eccezione
*/
private function onClickButtonError(event:MouseEvent) : void {
throw new Error("ECCEZIONE NON GESTITA");
}
/**
* Invocato al termine del caricamento dell'applicazione
* Imposta il global exception handler
*/
private function applicationReady(event:FlexEvent) : void {
loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, uncaughtErrorManager);
}
/**
* Gestore globale delle eccezioni
*/
private function uncaughtErrorManager(e:UncaughtErrorEvent) : void {
//evito che venga mostrato il popup di errore del flash player
e.preventDefault();
//mostro un messaggio di errore
Alert.show("Si è verificato un errore!");
}
]]>
</fx:Script>
<s:Button x="50" y="70" label="Genera un errore!" width="150" height="60" id="buttonError"/>
</s:Application>
Il metodo preventDefault()
evita che venga mostrato il “brutto” popup di segnalazione di un'eccezione non gestita del Flash Player. Nell'esempio la segnalazione di errore è stata effettuata con l'alert manager. All'interno di questo metodo può essere inserito tutto il codice ritenuto opportuno dallo sviluppatore: può essere effettuato un log dell'errore o può essere forzato il riavvio dell'applicazione, ecc.
È disponibile per il download il progetto completo.
Cosa fare e cosa non fare
Utilizzare il Global Exception Handler è estremamente conveniente ma non deve essere una scusa per non gestire quando possibile le eccezioni. E' bene gestire sempre le eccezioni dalle quali si può recuperare uno stato consistente dell'applicazione, e riservare l'utilizzo del gestore globale a quelle situazioni inaspettate che causerebbero problemi seri all'applicazione.
Il gestore delle eccezioni dovrebbe segnalare in maniera user-friendly il verificarsi dell'errore e forzare l'uscita dall'applicazione o il suo riavvio. Inoltre, con questo meccanismo abbiamo la possibilità di effettuare un log della situazione di errore in modo da poter effettuare il bugfixing avendo a disposizione informazioni dettagliate sui problemi dell'applicazione che abbiamo sviluppato.