În acest tutorial, veți deveni familiar cu conceptul de reflecție Java: capacitatea unei clase sau a unui obiect de a examina detaliile despre implementarea sa în mod programatic.
Aplicațiile Android sunt scrise în Java, un limbaj de programare care acceptă reflecția - capacitatea unui obiect de a se examina. În acest tutorial veți învăța elementele de bază ale reflecției Java, inclusiv modalitatea de a inspecta metodele și câmpurile unei anumite clase, de a verifica disponibilitatea unor metode specifice și alte sarcini practice pe care ar trebui să le folosiți atunci când dezvoltați pentru diferite versiuni ale Android SDK.
Din punct de vedere tehnic, nu aveți nevoie de niciun fel de instrumente pentru a finaliza acest tutorial, dar cu siguranță veți avea nevoie de ele pentru a dezvolta aplicații Android.
Pentru a dezvolta aplicații Android (sau orice aplicații Java, de pildă), aveți nevoie de un mediu de dezvoltare pentru a scrie și a construi aplicații. Eclipse este un mediu de dezvoltare foarte popular (IDE) pentru Java și IDE preferat pentru dezvoltarea Android. Este disponibil gratuit pentru sistemele de operare Windows, Mac și Linux.
Pentru instrucțiuni complete despre cum să instalați Eclipse (inclusiv versiunile acceptate) și SDK-ul Android, consultați site-ul Web pentru dezvoltatorii Android.
Reflecția oferă dezvoltatorilor flexibilitatea de a inspecta și de a determina caracteristicile API în timpul execuției, în loc să compileze timpul. În cadrul constrângerilor de securitate impuse de Java (de exemplu, utilizarea publicului, protejat, privat), puteți construi obiecte, câmpuri de acces și puteți invoca dinamic metode. API-urile Java Reflection sunt disponibile ca parte a pachetului java.lang.reflect, care este inclus în SDK-ul Android pentru dezvoltatori de utilizat.
Deci, ce are de-a face cu dezvoltarea Android? Ei bine, cu fiecare versiune nouă a SDK Android, clase, interfețe, metode etc. sunt adăugate, actualizate și (mai puțin frecvent) eliminate. Cu toate acestea, dezvoltatorii Android doresc adesea să direcționeze dispozitive care rulează diferite versiuni de Android cu un pachet de aplicații simplu. Pentru a face acest lucru, dezvoltatorii Android pot folosi tehnici de reflecție pentru a determina, în timpul rulării, dacă o anumită clasă sau metodă este disponibilă înainte de a încerca să o utilizați. Acest lucru permite dezvoltatorului să utilizeze noile API acolo unde este disponibil, sprijinind în același timp dispozitive mai vechi - toate în aceeași aplicație.
Clasele Java sunt reprezentate la runtime folosind clasa (java.lang.Class). Această clasă oferă punctul de plecare pentru toate API-urile de reflecție. În cadrul acestei clase veți găsi multe metode pentru inspectarea diferitelor aspecte ale unei clase, cum ar fi câmpurile, constructorii, metodele, permisiunile și multe altele. De asemenea, puteți utiliza metoda de clasă numită forName () pentru a încărca o clasă non-primitivă (de exemplu, nu int, dar Integer) după nume dinamic în timpul execuției, în loc de la momentul compilării:
String sClassName = "android.app.NotificationManager"; încercați class classToInvestigate = Class.forName (sClassName); // Dinamic faceți chestii cu această clasă // Listează constructori, câmpuri, metode etc. catch (ClassNotFoundException e) // Clasa nu a fost găsită! captură (Excepție e) // Excepție necunoscută
Clasa (în acest caz, NotificationManager) nu trebuie să aibă declarația de import corespunzătoare în codul dvs.; nu compilați în această clasă în aplicația dvs. În schimb, încărcătorul de clasă va încărca clasa dinamic în timpul rulării, dacă este posibil. Puteți apoi să inspectați acest obiect Clasă și să utilizați tehnicile de reflexie descrise în restul acestui tutorial.
Puteți examina constructorii disponibili într-o anumită clasă. Pentru a obține doar constructorii disponibili publicului, utilizați getConstructors (). Cu toate acestea, dacă doriți să inspectați acele metode specificate în cadrul clasei, indiferent dacă sunt publice sau nu, utilizați în schimb getDeclaredConstructors (). Ambele metode returnează o serie de obiecte Constructor (java.lang.reflect.Constructor).
De exemplu, următorul cod se repetă prin constructorii declarați ai unei clase:
Constructorul [] aClassConstructors = classToInvestigate.getDeclaredConstructors (); pentru (Constructor c: aClassConstructors) // S-a găsit un constructor c
Odată ce aveți un obiect constructor valid, puteți să-i inspectați parametrii și chiar să declarați o nouă instanță a clasei utilizând acel constructor cu metoda newInstance ().
Puteți examina câmpurile (sau atributele) disponibile într-o anumită clasă. Pentru a obține doar metodele disponibile public, inclusiv câmpurile moștenite, utilizați getFields (). Cu toate acestea, dacă doriți să inspectați acele câmpuri specificate în clasă (și nu cele moștenite), indiferent dacă sunt publice sau nu, utilizați în schimb getDeclaredFields (). Ambele metode returnează o serie de obiecte Field (java.lang.reflect.Field).
De exemplu, următorul cod iterează prin câmpurile declarate ale unei clase:
Câmpul [] aClassFields = classToInvestigate.getDeclaredFields (); pentru (câmpul f: aClassFields) // a găsit un câmp f
De asemenea, puteți verifica un anumit câmp public după nume folosind metoda getField (). De exemplu, pentru a verifica câmpul EXTRA_CHANGED_PACKAGE_LIST din clasa Intent (care a fost adăugat în API Level 8 sau Android 2.2), puteți utiliza:
String sClassName = "android.content.Intent"; încercați class classToInvestigate = Class.forName (sClassName); String strNewFieldName = "EXTRA_CHANGED_PACKAGE_LIST"; Câmp newIn22 = classToInvestigate.getField (strNewFieldName); captură (ClassNotFoundException e) // Class not found catch (NoSuchFieldException e) // Câmpul nu există, probabil că suntem pe Android 2.1 sau mai vechi // oferă funcționalități alternative pentru a suporta dispozitive mai vechi captură (SecurityException e) // Acces interzis! captură (Excepție e) // Excepție necunoscută
După ce aveți un obiect Field valid, puteți obține numele acestuia utilizând metoda toGenericString (). Dacă aveți permisiunile corespunzătoare, puteți accesa, de asemenea, valoarea acelui câmp de clasă utilizând metodele get () și set ().
Puteți examina metodele disponibile într-o anumită Clasă. Pentru a obține doar metodele disponibile public, inclusiv metode moștenite, utilizați getMethods (). Cu toate acestea, dacă doriți să inspectați acele metode specificate în cadrul clasei (fără cele moștenite), indiferent dacă sunt publice sau nu, utilizați în schimb getDeclaredMethods (). Ambele metode returnează o serie de obiecte Metoda (java.lang.reflect.Method).
De exemplu, următorul cod se iterează prin metodele declarate ale unei clase:
Metoda [] aClassMethods = classToInvestigate.getDeclaredMethods (); pentru (Metoda m: aClassMethods) // S-a găsit o metodă m
Odată ce aveți un obiect Metodă valid, puteți obține numele acestuia utilizând metoda toGenericString (). De asemenea, puteți examina parametrii utilizați de metodă și excepțiile pe care le poate arunca. În cele din urmă, dacă aveți permisiunile corespunzătoare, puteți apela metoda folosind metoda invoke ().
Puteți examina clasele interioare definite într-o clasă folosind metoda getDeclaredClasses (). Această metodă va returna o matrice de obiecte Clasă (java.lang.class) declarate în clasa părinte. Aceste clase pot fi apoi inspectate ca oricare alta.
De asemenea, puteți inspecta steagurile și setările de securitate, numite modifiers, asociate cu o anumită clasă, câmp sau metodă, folosind metoda getModifiers (). Modificatorii interesanți includ dacă componenta este publică, privată, protejată, abstractă, finală sau statică (printre altele).
De exemplu, următorul cod verifică modificatorii de securitate ai unei clase:
int permisiuni = classToInvestigate.getModifiers (); dacă (Modifier.isPublic (permisiuni)) // Clasa este publică dacă (Modifier.isProtected (permisiunile)) // Class is Protected dacă (Modifier.isPrivate (permisiunile)
Rețineți că nu puteți accesa dinamic sau invoca orice clasă, metodă sau câmp folosind reflecții pe care nu le-ați putea accesa în mod obișnuit la timpul de compilare. Cu alte cuvinte, securitatea claselor obișnuite este încă aplicată în timpul rulării.
De asemenea, puteți examina adnotările numite metadate asociate cu o anumită clasă, câmp sau metodă utilizând metoda getAnnotations (). Metadatele interesante asociate cu o clasă pot include, printre altele, informații despre depreciere, avertismente și suprascrieri.
De exemplu, următorul cod verifică metadatele disponibile pentru clasa AbsoluteLayout. Deoarece această clasă a fost depreciată în Android 1.5, una dintre adnotările returnate este @ java.lang.Deprecated () când acest cod este rulat pe Android 2.2:
String sClassName = "android.widget.AbsoluteLayout"; încercați class classToInvestigate = Class.forName (sClassName); Adnotare [] aAnnotations = classToInvestigate.getDeclaredAnotations (); pentru (Adnotare a: aAnotări) // S-a găsit o adnotare, utilizați a.toString () pentru ao imprima catch (ClassNotFoundException e) // Clasa nu a fost găsită! captură (Excepție e) // Executați o excepție necunoscută!
În mod similar, ați putea verifica pur și simplu existența unei adnotări specifice, cum ar fi deprecierea, după tipul său:
dacă (classToInvestigate.isAnnotationPresent (java.lang.Deprecated.class) == true) // Clasa este depreciată!
De asemenea, puteți utiliza reflecția pentru a asista la depanare. De exemplu, este posibil să doriți să utilizați clasă cuvânt cheie pentru a accesa datele clasei de bază pentru un anumit tip:
importă android.app.Activity; ... String strClassName = Activity.class.getName (); // android.app.Activity
De asemenea, puteți obține informații de clasă dintr-o instanță variabilă folosind metoda getClass () a clasei Object (care este, prin urmare, moștenită de toate clasele din Java):
String prostie = "Stringul proastă!"; Clasa someKindOfClass = silly.getClass (); Stringul strSillyClassName = someKindOfClass.getName (); // java.lang.String
Dacă doriți să verificați clasa unei variabile, folosirea instanței este mai potrivită. Consultați tutorialul anterior din exemplul pentru mai multe detalii.
În mod similar, poate doriți să utilizați metoda getClass () cu acest cuvânt cheie pentru a verifica numele clasei în care vă aflați și să includeți aceste informații ca parte a logării la depanare la LogCat:
Stringul strCurrentClass = this.getClass (). GetName (); // de exemplu. activitatea curentă Log.v (strCurrentClass, "Eticheta de depanare este clasa curentă";);
După cum ați văzut, reflecția poate fi utilizată cu mare efect, mai ales atunci când nu sunteți siguri dacă o anumită clasă sau o metodă este disponibilă la timpul de compilare. Cu toate acestea, reflexia are unele dezavantaje, inclusiv reducerea performanței și pierderea practicii puternice de introducere și codificare sigură aplicată la momentul compilării. Cel mai bine este să folosiți cu ușurință reflecția, dar să o utilizați când este necesar.
Reflecția este un instrument puternic pe care dezvoltatorii Java îl pot utiliza pentru a explora programele și pachetele API în mod programabil. În timp ce operațiunile de reflecție vin la un cost, ele dau dezvoltatorului flexibilitatea care uneori este esențială pentru a-ți face treaba. Dezvoltatorii de aplicații Android utilizează frecvent aceste tehnici de reflecție simple pentru a testa disponibilitatea anumitor clase, interfețe, metode și câmpuri la timpul de execuție, permițându-le să suporte versiuni diferite.
Dezvoltatorii mobili Lauren Darcey și Shane Conder au coautorizat mai multe cărți despre dezvoltarea Android: o carte de programare în profunzime intitulată Dezvoltarea aplicațiilor fără fir Android și Sams TeachYourself Dezvoltarea de aplicații Android în 24 de ore. Când nu scriu, își petrec timpul dezvoltând software-ul mobil la compania lor și oferind servicii de consultanță. Acestea pot fi obținute prin e-mail la androidwirelessdev@[email protected], prin intermediul blogului lor la androidbook.blogspot.com, și pe Twitter @ androidwireless.