In questo tutorial, acquisirai familiarità con il concetto di riflessione Java: la capacità di una classe o di un oggetto di esaminare i dettagli sulla propria implementazione a livello di codice.
Le applicazioni Android sono scritte in Java, un linguaggio di programmazione che supporta la riflessione: la capacità di un oggetto di esaminarsi. In questo tutorial imparerai le basi della riflessione Java, tra cui come esaminare i metodi e i campi di una determinata classe, verificare la disponibilità di metodi specifici e altre attività pratiche che potresti dover utilizzare durante lo sviluppo per diverse versioni di l'SDK di Android.
Tecnicamente, non hai bisogno di strumenti per completare questo tutorial, ma sicuramente ti serviranno per sviluppare applicazioni Android.
Per sviluppare applicazioni Android (o qualsiasi altra applicazione Java, per quella materia), è necessario un ambiente di sviluppo per scrivere e creare applicazioni. Eclipse è un ambiente di sviluppo molto popolare (IDE) per Java e l'IDE preferito per lo sviluppo Android. È liberamente disponibile per i sistemi operativi Windows, Mac e Linux.
Per le istruzioni complete su come installare Eclipse (incluse le versioni supportate) e l'SDK Android, consultare il sito Web degli sviluppatori Android.
Reflection offre agli sviluppatori la flessibilità di ispezionare e determinare le caratteristiche dell'API in fase di runtime, anziché il tempo di compilazione. All'interno dei vincoli di sicurezza imposti da Java (ad esempio l'uso di pubblico, protetto, privato), è quindi possibile costruire oggetti, accedere ai campi e richiamare i metodi in modo dinamico. Le API di Java Reflection sono disponibili come parte del pacchetto java.lang.reflect, incluso nell'SDK Android per gli sviluppatori da utilizzare.
Quindi cosa c'entra questo con lo sviluppo di Android? Bene, con ogni nuova versione di Android SDK, le classi, le interfacce, i metodi, ecc. Vengono aggiunti, aggiornati e (meno frequentemente) rimossi. Tuttavia, gli sviluppatori Android spesso desiderano indirizzare dispositivi che eseguono versioni diverse di Android con un semplice pacchetto di applicazioni. Per fare ciò, gli sviluppatori Android possono utilizzare tecniche di riflessione per determinare, in fase di runtime, se una classe o un metodo specifico è disponibile prima di provare a utilizzarlo. Ciò consente allo sviluppatore di sfruttare le nuove API, laddove disponibili, pur continuando a supportare i dispositivi meno recenti, tutto nella stessa applicazione.
Le classi Java sono rappresentate in fase di esecuzione utilizzando la classe Class (java.lang.Class). Questa classe fornisce il punto di partenza per tutte le API di reflection. All'interno di questa classe, troverai molti metodi per esaminare diversi aspetti di una classe, come i suoi campi, costruttori, metodi, permessi e altro ancora. Puoi anche utilizzare il metodo Class chiamato forName () per caricare una classe non primitiva (ad esempio non int, ma Integer) per nome dinamicamente in fase di esecuzione, anziché in fase di compilazione:
String sClassName = "android.app.NotificationManager"; prova Class classToInvestigate = Class.forName (sClassName); // Fa dinamicamente cose con questa classe // Costruisci, campi, metodi, ecc. List catch (ClassNotFoundException e) // Class not found! catch (Exception e) // Unknown exception
La classe (in questo caso, NotificationManager) non ha bisogno della corrispondente istruzione import nel tuo codice; non stai compilando in questa classe nella tua applicazione. Invece, il caricatore di classi caricherà la classe dinamicamente in fase di runtime, se possibile. È quindi possibile ispezionare questo oggetto di classe e utilizzare le tecniche di riflessione descritte nel resto di questo tutorial.
È possibile ispezionare i costruttori disponibili all'interno di una determinata classe. Per ottenere solo i costruttori che sono pubblicamente disponibili, utilizzare getConstructors (). Tuttavia, se si desidera esaminare quei metodi specificatamente dichiarati all'interno della classe, siano essi pubblici o meno, utilizzare invece getDeclaredConstructors (). Entrambi i metodi restituiscono una matrice di oggetti Constructor (java.lang.reflect.Constructor).
Ad esempio, il codice seguente itera attraverso i costruttori dichiarati di una classe:
Constructor [] aClassConstructors = classToInvestigate.getDeclaredConstructors (); for (Constructor c: aClassConstructors) // Trovato un costruttore c
Una volta che hai un oggetto Constructor valido, puoi controllare i suoi parametri e persino dichiarare una nuova istanza della classe usando quel costruttore con il metodo newInstance ().
È possibile ispezionare i campi (o attributi) disponibili all'interno di una determinata classe. Per ottenere solo i metodi che sono pubblicamente disponibili, inclusi i campi ereditati, usa getFields (). Tuttavia, se si desidera esaminare quei campi dichiarati specificamente all'interno della classe (e non quelli ereditati), che siano pubblici o meno, utilizzare invece getDeclaredFields (). Entrambi i metodi restituiscono una matrice di oggetti Field (java.lang.reflect.Field).
Ad esempio, il codice seguente itera attraverso i campi dichiarati di una classe:
Field [] aClassFields = classToInvestigate.getDeclaredFields (); for (Field f: aClassFields) // Trovato un campo f
Puoi anche controllare un campo pubblico specifico per nome usando il metodo getField (). Ad esempio, per verificare il campo EXTRA_CHANGED_PACKAGE_LIST della classe Intent (che è stato aggiunto in API Level 8 o Android 2.2), è possibile utilizzare:
String sClassName = "android.content.Intent"; prova Class classToInvestigate = Class.forName (sClassName); String strNewFieldName = "EXTRA_CHANGED_PACKAGE_LIST"; Field newIn22 = classToInvestigate.getField (strNewFieldName); catch (ClassNotFoundException e) // Classe non trovata catch (NoSuchFieldException e) // Il campo non esiste, probabilmente siamo su Android 2.1 o precedente // fornisce funzionalità alternative per supportare dispositivi più vecchi catch (SecurityException e) // Accesso negato! catch (Exception e) // Unknown exception
Una volta che hai un oggetto Field valido, puoi ottenere il suo nome usando il metodo toGenericString (). Se disponi delle autorizzazioni appropriate, puoi anche accedere al valore di quel campo di classe usando i metodi appropriati get () e set ().
È possibile esaminare i metodi disponibili all'interno di una determinata classe. Per ottenere solo i metodi che sono pubblicamente disponibili, inclusi i metodi ereditati, usa getMethods (). Tuttavia, se si desidera esaminare quei metodi specificatamente dichiarati all'interno della classe (senza ereditarietà), che siano pubblici o meno, utilizzare invece getDeclaredMethods (). Entrambi i metodi restituiscono una matrice di oggetti Method (java.lang.reflect.Method).
Ad esempio, il codice seguente itera attraverso i metodi dichiarati di una classe:
Method [] aClassMethods = classToInvestigate.getDeclaredMethods (); for (Metodo m: aClassMethods) // Trovato un metodo m
Una volta che hai un oggetto Method valido, puoi ottenere il suo nome usando il metodo toGenericString (). È inoltre possibile esaminare i parametri utilizzati dal metodo e le eccezioni che può generare. Infine, se disponi delle autorizzazioni appropriate, puoi anche chiamare il metodo usando il metodo invoke ().
È possibile ispezionare le classi interne definite all'interno di una classe utilizzando il metodo getDeclaredClasses (). Questo metodo restituirà una matrice di oggetti Class (java.lang.class) dichiarati all'interno della classe genitore. Queste classi possono quindi essere ispezionate come le altre.
È inoltre possibile controllare i flag e le impostazioni di sicurezza, denominati modificatori, associati a una determinata classe, campo o metodo utilizzando il metodo getModifiers (). Modificatori interessanti includono se il componente è pubblico, privato, protetto, astratto, finale o statico (tra gli altri).
Ad esempio, il codice seguente controlla i modificatori di sicurezza di una classe:
int permissions = classToInvestigate.getModifiers (); if (Modifier.isPublic (permissions)) // Class is Public if (Modifier.isProtected (permissions)) // Class is Protected if (Modifier.isPrivate (permissions)) // Class is Private
Tieni presente che non puoi accedere o richiamare dinamicamente alcuna classe, metodo o campo utilizzando il reflection a cui normalmente non potresti accedere in fase di compilazione. In altre parole, la sicurezza della classe regolare viene ancora applicata in fase di runtime.
È anche possibile esaminare i metadati, chiamati annotazioni, associati a una determinata classe, campo o metodo utilizzando il metodo getAnnotations (). Metadati interessanti associati a una classe potrebbero includere informazioni su deprecazione, avvisi e sostituzioni, tra le altre cose.
Ad esempio, il codice seguente controlla i metadati disponibili per la classe AbsoluteLayout. Poiché questa classe è stata deprecata in Android 1.5, una delle annotazioni restituite è @ java.lang.Deprecated () quando questo codice viene eseguito su Android 2.2:
String sClassName = "android.widget.AbsoluteLayout"; prova Class classToInvestigate = Class.forName (sClassName); Annotation [] aAnnotations = classToInvestigate.getDeclaredAnnotations (); for (Annotation a: aAnnotations) // Trovato un'annotazione, utilizzare a.toString () per stamparlo catch (ClassNotFoundException e) // Classe non trovata! catch (Exception e) // Gestisce l'eccezione sconosciuta!
Allo stesso modo, puoi semplicemente verificare l'esistenza di un'annotazione specifica, come la deprecazione, per il suo tipo:
if (classToInvestigate.isAnnotationPresent (java.lang.Deprecated.class) == true) // La classe è deprecata!
È inoltre possibile utilizzare la riflessione per facilitare il debug. Ad esempio, potresti voler usare il classe parola chiave per accedere ai dati della classe sottostante per un dato tipo:
import android.app.Activity; ... String strClassName = Activity.class.getName (); // android.app.Activity
Puoi anche ottenere informazioni sulla classe da un'istanza variabile usando il metodo getClass () della classe Object (che è quindi ereditata da tutte le classi in Java):
String silly = "Silly String!"; Class someKindOfClass = silly.getClass (); String strSillyClassName = someKindOfClass.getName (); // java.lang.String
Se si desidera verificare la classe di una variabile, utilizzare instanceof è più appropriato. Vedi il precedente tutorial su instanceof per maggiori dettagli.
Allo stesso modo, potresti voler utilizzare il metodo getClass () con questa parola chiave per verificare il nome della classe in cui ti trovi e includere queste informazioni come parte della registrazione di debug su LogCat:
String strCurrentClass = this.getClass (). GetName (); // per esempio. l'attuale Activity Log.v (strCurrentClass, "Tag di debug è la classe corrente.");
Come hai visto, la reflection può essere usata con grande effetto, specialmente quando non sei sicuro se una classe o un metodo specifico è disponibile in fase di compilazione. La riflessione, tuttavia, presenta alcuni inconvenienti, tra cui la riduzione delle prestazioni e la perdita della forte tipizzazione e delle pratiche di codifica sicure applicate in fase di compilazione. È meglio usare la riflessione con parsimonia, ma usarla quando è necessario.
Reflection è un potente strumento che gli sviluppatori Java possono utilizzare per esplorare pacchetti e API a livello di codice in fase di runtime. Mentre le operazioni di riflessione hanno un costo, danno allo sviluppatore la flessibilità che a volte è essenziale per portare a termine il lavoro. Gli sviluppatori Android utilizzano spesso queste semplici tecniche di riflessione per verificare la disponibilità di classi, interfacce, metodi e campi specifici in fase di runtime, consentendo loro di supportare versioni diverse.
Gli sviluppatori mobili Lauren Darcey e Shane Conder hanno coautore diversi libri sullo sviluppo di Android: un libro di programmazione approfondito intitolato Sviluppo di applicazioni wireless Android e Sams TeachYourself Sviluppo di applicazioni Android in 24 ore. Quando non scrivono, passano il loro tempo a sviluppare software mobile presso la loro azienda ea fornire servizi di consulenza. Possono essere contattati via email a [email protected], tramite il loro blog su androidbook.blogspot.com e su Twitter @androidwireless.