I denne opplæringen blir du kjent med begrepet Java refleksjon: evnen til en klasse eller objekt å undersøke detaljer om sin egen implementering programmatisk.
Android-programmer er skrevet i Java, et programmeringsspråk som støtter refleksjon - et objekts evne til å undersøke seg selv. I denne opplæringen lærer du grunnleggende om Java refleksjon, blant annet hvordan du inspiserer metoder og felt i en bestemt klasse, kontroller tilgjengeligheten av spesifikke metoder og andre praktiske oppgaver du må bruke når du utvikler for ulike versjoner av Android SDK.
Teknisk trenger du ikke noe verktøy for å fullføre denne opplæringen, men du vil sikkert trenge dem til å utvikle Android-applikasjoner.
For å utvikle Android-applikasjoner (eller Java-programmer, for øvrig), trenger du et utviklingsmiljø for å skrive og bygge programmer. Eclipse er et svært populært utviklingsmiljø (IDE) for Java og den foretrukne IDE for Android-utvikling. Det er fritt tilgjengelig for Windows, Mac og Linux operativsystemer.
For fullstendige instruksjoner om hvordan du installerer Eclipse (inkludert hvilke versjoner som støttes) og Android SDK, se Android-utviklerens nettsted.
Refleksjon gir utviklere fleksibiliteten til å inspisere og bestemme API-egenskaper ved kjøring, i stedet for å kompilere tid. Innenfor sikkerhetsbegrensningene som er pålagt av Java (for eksempel bruk av offentlig, beskyttet, privat), kan du da konstruere objekter, tilgangsfelt og påkalle metoder dynamisk. Java Reflection APIs er tilgjengelig som en del av java.lang.reflect-pakken, som er inkludert i Android SDK for utviklere å bruke.
Så hva har dette å gjøre med Android-utvikling? Vel, med hver ny versjon av Android SDK, blir klasser, grensesnitt, metoder, etc. lagt til, oppdatert og (sjeldnere) fjernet. Imidlertid vil Android-utviklere ofte målrette mot enheter som kjører forskjellige versjoner av Android med en enkel applikasjonspakke. For å gjøre dette kan Android-utviklere bruke refleksjonsteknikker for å bestemme, ved kjøring, om en bestemt klasse eller metode er tilgjengelig før du prøver å bruke den. Dette gjør det mulig for utvikleren å utnytte nye APIer der det er tilgjengelig, samtidig som de støtter de eldre enhetene, alt i samme applikasjon.
Java-klasser er representert ved kjøretid ved hjelp av klassen (java.lang.Class). Denne klassen gir utgangspunktet for alle refleksjon-APIer. I denne klassen finner du mange metoder for å inspisere ulike aspekter av en klasse, for eksempel feltene, konstruktørene, metodene, tillatelsene og mer. Du kan også bruke klassemetoden som kalles forName () for å laste en ikke-primitiv klasse (for eksempel ikke int, men helhet) ved navn dynamisk ved kjøring, i stedet for å kompilere tid:
String sClassName = "android.app.NotificationManager"; prøv klasse classToInvestigate = Class.forName (sClassName); // Dynamisk gjøre ting med denne klassen // Liste konstruktører, felt, metoder, etc. fangst (ClassNotFoundException e) // Klasse ikke funnet! fangst (Unntak e) // Ukjent unntak
Klassen (i dette tilfellet NotificationManager) trenger ikke å ha tilsvarende importoppgave i koden din; Du samler ikke inn i denne klassen i søknaden din. I stedet vil klasselasteren laste klassen dynamisk ved kjøring, om mulig. Du kan deretter inspisere dette klassobjektet og bruke refleksjonsteknikkene beskrevet i resten av denne opplæringen.
Du kan inspisere konstruktørene som er tilgjengelige i en gitt klasse. For å få bare konstruktørene som er offentlig tilgjengelige, bruk getConstructors (). Men hvis du vil inspisere de metodene som er spesifikt deklarert i klassen, enten de er offentlige eller ikke, bruker du getDeclaredConstructors () i stedet. Begge metodene returnerer en rekke Constructor (java.lang.reflect.Constructor) objekter.
For eksempel, den følgende koden iterates gjennom de deklarerte konstruktørene til en klasse:
Constructor [] aClassConstructors = classToInvestigate.getDeclaredConstructors (); for (Constructor c: aClassConstructors) // Funnet en konstruktør c
Når du har et gyldig Constructor-objekt, kan du inspisere parametrene og til og med erklære en ny forekomst av klassen ved hjelp av den konstruktøren med metoden newInstance ().
Du kan inspisere feltene (eller attributter) som er tilgjengelige i en gitt klasse. For å få bare de metodene som er offentlig tilgjengelige, inkludert arvelige felt, bruk getFields (). Men hvis du vil inspisere de feltene som er spesifikt deklarert i klassen (og ikke arvelige), enten de er offentlige eller ikke, bruk getDeclaredFields () i stedet. Begge metodene returnerer en rekke feltfelt (java.lang.reflect.Field) objekter.
For eksempel, den følgende koden iterates gjennom de deklarerte feltene i en klasse:
Felt [] aClassFields = classToInvestigate.getDeclaredFields (); for (Felt f: aClassFields) // Funnet et felt f
Du kan også sjekke for et bestemt offentlig felt etter navn ved hjelp av getField () -metoden. For eksempel, for å se etter EXTRA_CHANGED_PACKAGE_LIST-feltet i Intent-klassen (som ble lagt til i API-nivå 8 eller Android 2.2), kan du bruke:
String sClassName = "android.content.Intent"; prøv klasse classToInvestigate = Class.forName (sClassName); String strNewFieldName = "EXTRA_CHANGED_PACKAGE_LIST"; Felt newIn22 = classToInvestigate.getField (strNewFieldName); Fangst (ClassNotFoundException e) // Klassen ikke funnet fangst (NoSuchFieldException e) // Feltet eksisterer ikke, sannsynligvis er vi på Android 2.1 eller eldre // Gi alternativ funksjonalitet for å støtte eldre enheter Fangst (SecurityException e) // Ingen tilgang! fangst (Unntak e) // Ukjent unntak
Når du har et gyldig feltobjekt, kan du få navnet sitt ved å bruke metoden toGenericString (). Hvis du har de riktige tillatelsene, kan du også få tilgang til verdien av det klassefeltet ved hjelp av de riktige get () og set () metodene.
Du kan inspisere metodene som er tilgjengelige i en gitt klasse. For å få bare de metodene som er offentlig tilgjengelige, inkludert arvelige metoder, bruk getMethods (). Men hvis du vil inspisere de metodene som er spesifikt deklarert i klassen (uten arvelige), om de er offentlige eller ikke, bruk getDeclaredMethods () i stedet. Begge metodene returnerer en rekke Metod (java.lang.reflect.Method) objekter.
For eksempel, den følgende koden iterates gjennom de deklarerte metodene til en klasse:
Metode [] aClassMethods = classToInvestigate.getDeclaredMethods (); for (Metode m: aClassMethods) // Funnet en metode m
Når du har et gyldig Metode-objekt, kan du få navnet sitt ved å bruke metoden toGenericString (). Du kan også undersøke parametrene som brukes av metoden og unntakene det kan kaste. Endelig, hvis du har de riktige tillatelsene, kan du også ringe metoden ved hjelp av invoke () -metoden.
Du kan inspisere de indre klassene definert i en klasse ved hjelp av getDeclaredClasses () -metoden. Denne metoden vil returnere en rekke klasser (java.lang.class) objekter deklarert i foreldreklassen. Disse klassene kan da inspiseres som alle andre.
Du kan også inspisere flaggene og sikkerhetsinnstillingene, som kalles modifikatorer, knyttet til en gitt klasse, felt eller metode ved hjelp av metoden getModifiers (). Interessante modifikatorer inkluderer om komponenten er offentlig, privat, beskyttet, abstrakt, endelig eller statisk (blant annet).
For eksempel kontrollerer følgende kode sikkerhetsmodifiseringene i en klasse:
int tillatelser = classToInvestigate.getModifiers (); hvis (Modifier.isPublic (tillatelser)) // Klassen er offentlig hvis (Modifier.isProtected (tillatelser)) // Klassen er beskyttet hvis (Modifier.isPrivat (tillatelser)) // Klassen er Privat
Husk at du ikke kan få dynamisk tilgang til eller påkalle noen klasse, metode eller felt ved hjelp av refleksjon som du normalt ikke ville ha tilgang til på kompileringstidspunktet. Med andre ord, er vanlig klassesikkerhet fortsatt brukt på kjøretid.
Du kan også inspisere metadata-kalt annoteringer som er knyttet til en gitt klasse, et felt eller en metode ved hjelp av metoden getAnnotations (). Interessante metadata knyttet til en klasse kan inkludere informasjon om avskrivning, advarsler og overstyringer, blant annet.
For eksempel kontrollerer følgende kode metadataene som er tilgjengelige for klassen AbsoluteLayout. Siden denne klassen ble utdatert i Android 1.5, er en av annotasjonene returnert @ java.lang.Deprecated () når denne koden kjøres på Android 2.2:
String sClassName = "android.widget.AbsoluteLayout"; prøv klasse classToInvestigate = Class.forName (sClassName); Annotasjon [] aAnnotations = classToInvestigate.getDeclaredAnnotations (); for (Anmerkning a: aAnnotasjoner) // Funnet en kommentar, bruk a.toString () for å skrive den ut catch (ClassNotFoundException e) // Klasse ikke funnet! fangst (Unntak e) // Håndter ukjent unntak!
På samme måte kan du bare se etter eksistensen av en spesifikk merknad, for eksempel avskrivning, av sin type:
hvis (classToInvestigate.isAnnotationPresent (java.lang.Deprecated.class) == true) // klasse er utdatert!
Du kan også bruke refleksjon for å hjelpe deg med feilsøking. For eksempel vil du kanskje bruke klasse søkeord for å få tilgang til underliggende klassedata for en gitt type:
importer android.app.Activity; ... String strClassName = Activity.class.getName (); // android.app.Activity
Du kan også få klassedata fra et variabelt eksempel ved hjelp av metoden getClass () i Objeklassen (som derfor er arvet av alle klasser i Java):
String dumt = "Silly String!"; Klasse someKindOfClass = silly.getClass (); String strSillyClassName = someKindOfClass.getName (); // java.lang.String
Hvis du vil sjekke klassen av en variabel, er bruk av instanceof mer hensiktsmessig. Se forrige veiledning for eksempel for flere detaljer.
På samme måte vil du kanskje bruke metoden getClass () med dette søkeordet for å sjekke navnet på klassen du befinner deg i, og ta med denne informasjonen som en del av feilsøkingsloggen til LogCat:
String strCurrentClass = this.getClass (). GetName (); // f.eks. den nåværende aktivitetsloggen.v (strCurrentClass, "Debug tag er nåværende klasse.");
Som du har sett, kan refleksjon brukes til stor effekt, spesielt når du er usikker på om en bestemt klasse eller metode er tilgjengelig på kompileringstidspunktet. Refleksjon har imidlertid noen ulemper, inkludert redusert ytelse og tap av sterke typing og sikker koding praksis håndhevet på kompileringstidspunktet. Det er best å bruke refleksjon sparsomt, men bruk det når det trengs.
Refleksjon er et kraftig verktøy som Java-utviklere kan bruke til å utforske pakker og APIer programmatisk ved kjøring. Mens refleksjonstransaksjoner kommer til en pris, gir de utvikleren fleksibiliteten som noen ganger er viktig for å få jobben gjort. Android-utviklere bruker ofte disse enkle refleksjonsteknikker for å teste for tilgjengeligheten av bestemte klasser, grensesnitt, metoder og felt ved kjøring, slik at de kan støtte forskjellige versjoner.
Mobilutviklere Lauren Darcey og Shane Conder har medforfatter flere bøker om Android-utvikling: en grundig programmeringsbok med tittel Android Wireless Application Development og Sams TeachYourself Android Application Development i 24 timer. Når de ikke skriver, bruker de sin tid på å utvikle mobil programvare hos deres firma og tilby konsulenttjenester. De kan nås via e-post til [email protected], via bloggen deres på androidbook.blogspot.com, og på Twitter @androidwireless.