Leer Java voor Android Development Reflection Basics

In deze tutorial zul je vertrouwd raken met het concept van Java-reflectie: het vermogen van een klasse of object om details over de eigen implementatie programmatisch te onderzoeken.

Android-applicaties zijn geschreven in de Java, een programmeertaal die reflectie ondersteunt - het vermogen van een object om zichzelf te onderzoeken. In deze zelfstudie leer je de basisprincipes van Java-reflectie, inclusief het inspecteren van de methoden en velden van een bepaalde klasse, het controleren op de beschikbaarheid van specifieke methoden en andere praktische taken die je mogelijk moet gebruiken bij het ontwikkelen voor verschillende versies van de Android SDK.

Wat je nodig hebt

Technisch gezien heb je geen tools nodig om deze tutorial te voltooien, maar je zult ze zeker nodig hebben om Android-applicaties te ontwikkelen.

Om Android-applicaties (of welke Java-applicaties dan ook) te ontwikkelen, hebt u een ontwikkelomgeving nodig om applicaties te schrijven en te bouwen. Eclipse is een zeer populaire ontwikkelomgeving (IDE) voor Java en de geprefereerde IDE voor Android-ontwikkeling. Het is gratis beschikbaar voor Windows-, Mac- en Linux-besturingssystemen.

Zie de Android-ontwikkelaarswebsite voor volledige instructies over het installeren van Eclipse (inclusief welke versies worden ondersteund) en de Android SDK.

Waarom Reflectie gebruiken?

Reflectie biedt ontwikkelaars de flexibiliteit om API-kenmerken tijdens runtime te inspecteren en te bepalen, in plaats van compileertijd. Binnen de beveiligingsbeperkingen die worden opgelegd door Java (bijvoorbeeld gebruik van public, protected, private), kunt u vervolgens objecten dynamisch construeren, toegang krijgen tot velden en methoden aanroepen. De Java Reflection API's zijn beschikbaar als onderdeel van het pakket java.lang.reflect, dat is opgenomen in de Android SDK voor ontwikkelaars om te gebruiken.

Dus wat heeft dit met Android-ontwikkeling te maken? Welnu, met elke nieuwe versie van de Android SDK worden klassen, interfaces, methoden, enz. Toegevoegd, bijgewerkt en (minder vaak) verwijderd. Android-ontwikkelaars willen echter vaak apparaten met verschillende versies van Android targeten met een eenvoudig toepassingspakket. Om dit te doen, kunnen Android-ontwikkelaars reflectietechnieken gebruiken om tijdens runtime te bepalen of een specifieke klasse of methode beschikbaar is voordat deze wordt gebruikt. Hierdoor kan de ontwikkelaar nieuwe API's gebruiken waar deze beschikbaar zijn en tegelijkertijd de oudere apparaten ondersteunen, allemaal in dezelfde applicatie.

Klassen inspecteren

Java-klassen worden tijdens runtime weergegeven met behulp van de klasse Class (java.lang.Class). Deze klasse biedt het startpunt voor alle reflectie-API's. In deze klasse vindt u veel methoden voor het inspecteren van verschillende aspecten van een klasse, zoals de velden, constructors, methoden, machtigingen en meer. U kunt ook de Class-methode met de naam forName () gebruiken om een ​​niet-primitieve klasse (bijvoorbeeld niet int, maar een geheel getal) dynamisch op naam te laden tijdens runtime in plaats van tijdens het compileren:

 String sClassName = "android.app.NotificationManager"; probeer Class classToInvestigate = Class.forName (sClassName); // Dynamisch dingen doen met deze klasse // Lijstconstructeurs, velden, methoden, etc. catch (ClassNotFoundException e) // Class not found!  catch (Uitzondering e) // Onbekende uitzondering 

De klasse (in dit geval NotificationManager) hoeft niet de bijbehorende importinstructie in uw code te hebben; je compileert niet in deze klas in je applicatie. In plaats daarvan laadt de klasse loader de klasse indien mogelijk dynamisch tijdens runtime. U kunt dit Klasse-object vervolgens inspecteren en de reflectietechnieken gebruiken die worden beschreven in de rest van deze zelfstudie.

Inspecteren van de constructeurs beschikbaar binnen een klasse

U kunt de constructeurs die beschikbaar zijn binnen een bepaalde klasse inspecteren. Gebruik getConstructors () om alleen de constructors te krijgen die voor het publiek beschikbaar zijn. Als u echter de specifiek aangegeven methoden binnen de klasse wilt inspecteren, ongeacht of deze openbaar zijn of niet, gebruikt u getDeclaredConstructors () in plaats daarvan. Beide methoden retourneren een array van Constructor-objecten (java.lang.reflect.Constructor).

De volgende code doorloopt bijvoorbeeld de gedeclareerde constructors van een klasse:

 Constructor [] aClassConstructors = classToInvestigate.getDeclaredConstructors (); for (Constructor c: aClassConstructors) // Een constructor gevonden c 

Zodra u een geldig object Constructor heeft, kunt u de parameters ervan inspecteren en zelfs een nieuw exemplaar van de klasse declareren met die constructor met de methode newInstance ().

Inspecteren van de velden die beschikbaar zijn binnen een klasse

U kunt de velden (of kenmerken) bekijken die beschikbaar zijn binnen een bepaalde klasse. Gebruik getFields () om alleen de methoden te krijgen die openbaar beschikbaar zijn, inclusief overerfde velden. Als u echter de velden wilt inspecteren die specifiek binnen de klasse zijn gedeclareerd (en niet overgeërfd), of deze nu openbaar zijn of niet, gebruikt u getDeclaredFields () in plaats daarvan. Beide methoden retourneren een reeks Field-objecten (java.lang.reflect.Field).

De volgende code doorloopt bijvoorbeeld de aangegeven velden van een klasse:

 Field [] aClassFields = classToInvestigate.getDeclaredFields (); for (Field f: aClassFields) // Een veld gevonden f 

U kunt ook op naam van een specifiek openbaar veld zoeken met de methode getField (). Als u bijvoorbeeld wilt controleren op het veld EXTRA_CHANGED_PACKAGE_LIST van de klasse Intent (die is toegevoegd in API Level 8 of Android 2.2), kunt u het volgende gebruiken:

 String sClassName = "android.content.Intent"; probeer Class classToInvestigate = Class.forName (sClassName); String strNewFieldName = "EXTRA_CHANGED_PACKAGE_LIST"; Veld newIn22 = classToInvestigate.getField (strNewFieldName);  catch (ClassNotFoundException e) // Klasse niet gevonden catch (NoSuchFieldException e) // Field bestaat niet, waarschijnlijk zijn we op Android 2.1 of ouder // bieden alternatieve functionaliteit om oudere apparaten te ondersteunen catch (SecurityException e)  // Toegang geweigerd!  catch (Uitzondering e) // Onbekende uitzondering 

Zodra u een geldig veld-object hebt, kunt u de naam ervan vinden met behulp van de methode toGenericString (). Als u over de juiste machtigingen beschikt, kunt u ook toegang krijgen tot de waarde van dat klassenveld met de juiste methoden get () en set ().

Inspecteren van de methoden beschikbaar binnen een klasse

U kunt de beschikbare methoden in een bepaalde klasse inspecteren. Gebruik getMethods () om alleen de methoden te krijgen die openbaar beschikbaar zijn, inclusief overgenomen methoden. Als u echter de methoden wilt inspecteren die specifiek zijn gedeclareerd in de klasse (zonder overgeërfde), of deze nu openbaar zijn of niet, gebruikt u getDeclaredMethods () in plaats daarvan. Beide methoden retourneren een array met methoden (java.lang.reflect.Method).

De volgende code doorloopt bijvoorbeeld de gedeclareerde methoden van een klasse:

 Methode [] aClassMethods = classToInvestigate.getDeclaredMethods (); for (Methode m: aClassMethods) // Een methode gevonden m 

Zodra u een geldig Method-object hebt, kunt u de naam ervan vinden met behulp van de methode toGenericString (). U kunt ook de parameters bekijken die door de methode worden gebruikt en de uitzonderingen die het kan opleveren. Als u ten slotte de juiste machtigingen hebt, kunt u de methode ook aanroepen met behulp van de methode invoke ().

Inspecteren van binnenklassen

U kunt de binnenklassen die in een klasse zijn gedefinieerd inspecteren met de methode getDeclaredClasses (). Met deze methode wordt een array Class-objecten (java.lang.class) geretourneerd die zijn gedeclareerd in de bovenliggende klasse. Deze klassen kunnen vervolgens als elk ander worden geïnspecteerd.

Inspecterende lid-modifiers

U kunt ook de vlaggen en beveiligingsinstellingen, de zogenaamde modifiers, die bij een bepaalde klasse, veld of methode horen, controleren met de methode getModifiers (). Interessante modifiers zijn onder andere of het component openbaar, privé, beschermd, abstract, definitief of statisch (onder andere) is.

De volgende code controleert bijvoorbeeld de beveiligingsmodifiers van een klasse:

 int permissions = classToInvestigate.getModifiers (); if (Modifier.isPublic (machtigingen)) // Class is openbaar if (Modifier.isProtected (rechten)) // Class is protected if (Modifier.isPrivate (machtigingen)) // Class is privé 

Onthoud dat je niet dynamisch toegang kunt krijgen tot een klasse, methode of veld en deze niet kunt gebruiken met behulp van reflectie die je normaal niet zou kunnen gebruiken tijdens het compileren. Met andere woorden, reguliere klassebeveiliging wordt nog steeds toegepast tijdens runtime.

Inspecteren van klassenmetadata

U kunt ook de metadata inspecteren - genaamd annotaties - die zijn gekoppeld aan een gegeven klasse, veld of methode met de methode getAnnotations (). Interessante metadata die aan een klasse zijn gekoppeld, kunnen onder andere informatie bevatten over beëindiging, waarschuwingen en opheffingen.

De volgende code controleert bijvoorbeeld de beschikbare metadata voor de AbsoluteLayout-klasse. Aangezien deze klasse is verouderd in Android 1.5, is een van de teruggezonden annotaties @ java.lang.Deprecated () wanneer deze code wordt uitgevoerd op Android 2.2:

 String sClassName = "android.widget.AbsoluteLayout"; probeer Class classToInvestigate = Class.forName (sClassName); Annotatie [] aAnnotations = classToInvestigate.getDeclaredAnnotations (); for (Annotation a: aAnnotations) // Een aantekening gevonden, gebruik a.toString () om het af te drukken catch (ClassNotFoundException e) // Class not found!  catch (Uitzondering e) // Omgaan met onbekende uitzondering!  

Op dezelfde manier kunt u eenvoudig controleren op het bestaan ​​van een specifieke annotatie, zoals afschrijvingen, op het type:

 if (classToInvestigate.isAnnotationPresent (java.lang.Deprecated.class) == true) // Klasse is verouderd!  

Reflectie: handig voor foutopsporing

U kunt reflectie ook gebruiken om te helpen bij foutopsporing. U wilt bijvoorbeeld de klasse sleutelwoord om toegang te krijgen tot de onderliggende klassegegevens voor een bepaald type:

 import android.app.Activity; ... String strClassName = Activity.class.getName (); // android.app.Activity 

U kunt ook klassegegevens ophalen van een variabel exemplaar met de methode getClass () van de klasse Object (die daarom wordt overgenomen door alle klassen in Java):

 String silly = "Silly String!"; Class someKindOfClass = silly.getClass (); String strSillyClassName = someKindOfClass.getName (); // java.lang.String 

Als u de klasse van een variabele wilt controleren, is het gebruik van instanceof meer geschikt. Zie de vorige tutorial op instanceof voor meer details.

U kunt ook de methode getClass () gebruiken met dit trefwoord om de naam van de klasse waarin u zich momenteel bevindt te controleren en deze informatie opnemen als onderdeel van uw logboekregistratie voor foutopsporing naar LogCat:

 String strCurrentClass = this.getClass (). GetName (); // b.v. de huidige activiteit Log.v (strCurrentClass, "Foutopsporingstag is huidige klasse."); 

Waarom niet om reflectie te gebruiken

Zoals je hebt gezien, kan reflectie met groot effect worden gebruikt, vooral als je niet zeker weet of een specifieke klasse of methode beschikbaar is tijdens het compileren. Reflectie heeft echter een aantal nadelen, waaronder verminderde prestaties en het verlies van de sterke typ- en veilige codeermethoden die werden afgedwongen tijdens het compileren. Het is het beste om spaarzaam met reflectie te werken, maar gebruik het wanneer dat nodig is.

Afsluiten

Reflection is een krachtige tool die Java-ontwikkelaars kunnen gebruiken om pakketten en API's tijdens runtime programmatisch te verkennen. Hoewel reflectiewerkzaamheden kosten met zich meebrengen, geven ze de ontwikkelaar de flexibiliteit die soms essentieel is om de klus te klaren. Android-ontwikkelaars gebruiken deze eenvoudige reflectietechnieken vaak om te testen op de beschikbaarheid van specifieke klassen, interfaces, methoden en velden tijdens runtime, waardoor ze verschillende versies kunnen ondersteunen.

Over de Auteurs

Mobiele ontwikkelaars Lauren Darcey en Shane Conder hebben samen meerdere boeken geschreven over Android-ontwikkeling: een diepgaand programmeerboek getiteld Android Wireless Application Development en Sams TeachYourself Android Application Development binnen 24 uur. Wanneer ze niet schrijven, besteden ze hun tijd aan het ontwikkelen van mobiele software bij hun bedrijf en het leveren van consultingservices. Ze zijn te bereiken via e-mail naar [email protected], via hun blog op androidbook.blogspot.com, en op Twitter @androidwireless.

Meer hulp nodig bij het schrijven van Android-apps? Bekijk onze nieuwste boeken en bronnen!