Pentru a intelege arhitectura unei aplicatii mobile Android este nevoie de un minim de cunostinte cu privire la conceptele cheie ale aplicatiilor Android. Intelegerea acestor elemente va permite programatorului sa controleze:
- componentele aplicatiei;
- ciclul de viata al aplicatiei;
- resursele aplicatiei.
In acest articol sunt descrise toate aceste concepte cheie, pentru a evidentia rolul lor, utilitatea si importanta lor. Alte articole vor descrie in detaliu modul in care sunt utilizate pentru a dezvolta o aplicatie mobila Android.
Alte subiecte care fac parte din acest tutorial Android sunt accesibile prin intermediul articolului Tutorial Android – Descriere si cuprins.
Principalele caracteristici ale unei aplicatii Android (mai multe informatii pe developer.android.com) sunt:
- este o aplicatie Java executata de catre o masina virtuala Dalvik; masina virtuala (VM) Dalvik executa fisiere.dex, obtinute din fisiere .class Java;
- este distribuita intr-un pachet Android (Android Package), care este un fisier .apk;
- este executata de catre sistemul de operare Android (un Linux multi-utilizator), intr-un sandbox (cutie de nisip); fiecare aplicatie are un ID utilizator unic si resursele sale pot fi accesate numai de catre acel utilizator; fiecare aplicatie ruleaza in procesul Linux propriu si in propria instanta de masina virtuala; in ciuda acestui fapt, doua aplicatii diferite pot avea acelasi ID utilizator pentru a partaja resursele lor;
- operatiile critice (acces la Internet, citire/scriere date de contact, monitorizare SMS, acces la modulul GPS), pot fi restrictionate sau se poate solicita permisiunea utilizatorului, utilizând fisierul manifest de configurare a aplicatiei, AndroidManifest.xml; informatii mai detaliate cu privire la securitatea aplicatiei Android sunt disponibile la http://developer.android.com/ghid/teme/securitate/security.html;
- o aplicatie Android inseamna una sau mai multe activitati (o activitate poate fi asociata cu un ecran sau o fereastra, dar este mai mult decât atât) si procesul Linux; ciclul de viata al unei activitati nu este legata de ciclul de viata al procesului; activitatea poate rula chiar in cazul in care procesul sau nu mai exista (acesta situatie este diferita de aplicatiile C, C++ sau Java in cazul carora o aplicatie este un proces);
- o aplicatie poate executa o componenta (activitate) din alta aplicatie; componenta (activitatea) este executata de procesul de care apartine;
- o aplicatie Android NU are un punct unic de intrare, cum ar fi metoda main() in aplicatiile Java, C sau C++.
Componentele aplicatiei Android
Cele mai importante componente ale unei aplicatii Android sunt:
Activity (Activitate)
- reprezinta o interfata cu utilizatorul, fereastra sau formular;
- o aplicatie Android poate avea una sau mai multe activitati; de exemplu o aplicatie de tip Agenda poate avea o activitate pentru a gestiona contactele, o activitate pentru a gestiona intâlniri si una pentru a edita o intrare in agenda;
- fiecare Activitate are propriul sau ciclu de viata, independent de ciclul de viata al procesului asociat aplicatiei;
- fiecare activitate are propria stare si datele acesteia pot fi salvate sau restaurate;
- activitatile pot fi pornite de aplicatii diferite (daca este permis);
- are un ciclu de viata complex deoarece aplicatiile pot avea activitati multiple si doar una este in prim-plan; utilizând managerul de activitati, sistemul Android gestioneaza o stiva de activitati care se gasesc in diferite stari (pornire, in executie, intrerupta, oprita, distrusa);
- in SDK, Activitatea este implementata folosind o subclasa a clasei Activity care extinde clasa Context;
Intent (Intentie)
- reprezinta o entitate folosit pentru a descrie o operatiune care urmeaza sa fie executata; este un mesaj transmis catre o alta componenta pentru a anunta o operatiune;
- oarecum similar cu conceptul de event-handler din .NET sau Java.;
- un mesaj asincron utilizat pentru a activa activitati sau servicii;
- gestionata de o instanta a clasei Intent;
Service (Serviciu)
- un task care se executa in fundal, fara interactiunea directa cu utilizatorul;
- gestionata de o instanta a clasei Service;
Content provider (Furnizor sau manager de continut)
- un API folosit pentru a gestiona datele private ale aplicatiei;
- un sistem de management de date ce descrie o alternativa la sistemul de fisiere, baze de date SQLite sau orice alta solutie de stocare persistenta;
- implementata de o subclasa a clasei ContentProvider;
- o solutie pentru a partaja si controla (pe baza de permisiuni) transferul de date intre aplicatii (de exemplu, sistemul Android ofera un furnizor de continut pentru datele de contact);
Broadcast receiver
- o componenta care raspunde la anunturi difuzate (propagate) la nivel de sistem;
- oarecum similar cu conceptul de handler global (sau evenimente de sistem);
- implementata de o subclasa a clasei BroadcastReceiver.
Ciclul de viata al unei Activitati
Activitatea este una dintre cele mai importante componente (alaturi de servicii si broadcast receivers) ale unei aplicatii Android deoarece este strans legata de interfata cu utilizatorul. O activitate este utilizat pentru a gestiona interfata cu utilizatorul si este echivalenta cu fereastra sau formularul din aplicatiile desktop.
Intelegerea modului in care se controleaza activitatea va permite sa:
- utilizati ciclul de viata al activitatii pentru a crea, vizualiza, utiliza si a opri activitatile;
- salvati datele utilizatorului inainte ca activitatea sa fie oprita si le restaurati atunci când activitatea este readusa in prim-plan;
- creati aplicatii cu mai multe formulare sau activitati;
Ciclului de viata al unei Activitati descrie starea in care o activitate poate fi la un moment dat (denumirele sunt lasate intentionat in Engleza deoarece fac parte din vocabularul programatorului):
Running – Activitatea a fost creata (onCreate()), pornita (onStart()) si este afisata pe ecranul aparatului; in cazul in care activitatea a mai fost utilizata si aplicatia a salvat starea acesteia (onSaveInstanceState()), activitatea este reluata din acel punct (onRestoreInstanceState() si onResume()); in aceasta stare utilizatorul interactioneaza cu activitatea prin intermediul interfetei dispozitivului (tastatura, touchscreen, display);
Paused – Activitatea pierde prim-planul (onPause()), deoarece o alta activitate este executata, cum ar fi o fereastra de dialog; de asemenea, in cazul in care aparatul intra in modul sleep, activitatea este oprita temporar; activitatea isi poate relua executia (onResume()) si este plasata inapoi in prim-plan;
Stopped – Activitatea nu este mai in uz si pentru ca este oprita (onStop()) nu este vizibila; pentru a fi reactivata (ea deja exista), activitatea trebuie sa fi repornita (onRestart() si onStart()) si reluata (onResume());
Destroyed – Activitatea este distrusa (onDestroy()) si memoria sa eliberat, deoarece nu mai este necesara sau sistemul are nevoie de memorie suplimentara pentru rutinele proprii sau pentru alte activitati; deoarece managementul memoriei este un aspect important pentru sistemul de operare Linux al dispozitivului mobil, procesul care gazduieste o activitate intrerupta, oprita sau distrusa, poate fi terminat pentru a elibera memorie pentru noi activitati; doar procesele ce gestioneaza activitati ce ruleaza sunt protejate;
Dupa cum se poate observa in imaginea anterioara, Activitatea are mai multe stari intre care exista tranzitii clare. In ciuda faptului ca lucrurile pot arata complicat, in realitate ele sunt mult mai simple daca ne concentram pe urmatoarele elemente:
- o singura Activitatea poate fi in prim-plan la un moment dat;
- doar sistemul gestioneaza starile si tranzitiile unei Activitati si NU programatorul (nu in mod direct, deoarece atunci când se lanseaza o activitate noua se modifica implicit starea activitatii curente)
- IMPORTANT ! sistemul va anunta atunci când activitatea isi schimba starea prin intermediul handler-elor (metode de forma onXXX()) pentru evenimentele de tip tranzitie; ca programator, puteti adauga propriul cod prin supradefinirea acestor metode:
onCreate(Bundle) – apelata cand activitatea este creata; folosind argumentul metodei de tip Bundle exista posibilitatea sa restabiliti starea activitatii, care a fost salvata intr-o sesiune anterioara; dupa ce activitatea a fost creata, va fi pornita (onStart());
onStart() – apelata in cazul in care activitatea urmeaza sa fie afisata; din acest punct, activitatea poate veni in prim-plan (onResume()) sau ramane ascunsa in fundal (onStop());
onRestoreInstanceState(Bundle) – apelata in cazul in care activitatea este initializata cu datele dintr-o stare anterioara, ce a fost salvata; in mod implicit, sistemul restaureaza starea interfetei cu utilizatorul (starea controalelor vizuale, pozitia cursorului, etc);
onResume() – apelata cand activitatea este vizibila iar utilizatorul poate interactiona cu aceasta; din aceasta stare, activitatea poate fi plasata in fundal, devenind intrerupta (onPause());
onRestart() – apelata in cazul in care activitatea revine in prim-plan dintr-o stare oprita (stopped); dupa aceasta, activitate este pornita (onStart()) din nou;
onPause() – apelata atunci când sistemul aduce in prim-plan o alta activitate; activitatea curenta este mutata in fundal si mai târziu poate fi oprita (onStop()) sau repornita si afisata (onResume()); acesta este un moment bun pentru a salva datele aplicatiei intr-un mediu de stocare persistent (fisiere, baze de date) deoarece dupa aceasta faza activitatea poate fi terminata si distrusa fara a se anunta acest lucru.
onSaveInstanceState(Bundle) – apelata pentru a salva starea curenta a activitatii; in mod implicit, sistemul salveaza starea interfetei cu utilizatorul;
onStop() – apelata in cazul in care activitatea nu mai este utilizata si nu mai este vizibila deoarece o alta activitate interactioneaza cu utilizatorul; din acest punct, activitatea poate fi repornita (onRestart()) sau distrusa (onDestroy());
onDestroy() – apelata in cazul in care activitatea este distrusa, iar memoria sa eliberata; acest lucru se poate intâmpla in cazul in care sistemul necesita mai multa memorie sau daca programatorul termina explicit activitatea apeland metoda finish() din clasa Activity;
Deoarece tranzitiile dintre stari sunt descrise prin apeluri catre diferite metode de tipul onXXX(), ciclul de viata al unei Activitati poate fi descris de succesiunea posibila a acestor apeluri:
Pentru a supradefini metodele anterioare, trebuie sa se acorde atentie semnaturii acestora (numele metodei si lista de parametri). Este mai sigur sa folosesti adnotarea @Override pentru a solicita o validare din partea compilatorului (in cazul in care metoda definita nu supradefineste o metoda din clasa de baza, atunci veti obtine o eroare de compilare).
Pentru a supradefini onCreate() sau onPause(), solutia este:
public class SomeActivity extends Activity { // The activity is being created. @Override public void onCreate(Bundle savedInstanceState) { // DO NOT forget to call the base method super.onCreate(savedInstanceState); } // Another activity is put in the foreground. @Override protected void onPause() { // DO NOT forget to call the base method super.onPause(); } }
- Important !
- Atunci când se supradefineste una din metodele onXXX(), trebuie sa apelati (prima instructiune din metoda) forma metodei din clasa de baza (Activity), deoarece scopul acestei abordari este de a adauga propriul cod la evenimentele activitatii si NU de a le rescrie.
- Important !
- Dupa cum se poate observa in ultimele doua imagini, activitatea se poate gasi in stari, cum ar fi Stopped sau Destroyed, in care sistemul de operare poate termina procesul care gazduieste activitatea. Sistemul de operare va elibera memoria acupata de activitate, fara a apela metodele onStop() si onDestroy(). In concluzie, evenimentul anuntat de onPause() este momentul recomandat pentru a salva starea unei activitati inainte ca aceasta sa fie distrusa.
Resursele aplicatie Android
Applications use text strings, bitmaps, video and audio files to enhance the user experience. All these represent resources. Despite they are controlled from the source code they are external resources and the recommendation is to externalize (not storing them in the application code) them in order to support:
- internalization by providing different languages and format user interface
- application portability on different devices
Resources are stored in the res directory inside the Android project. In order to manage different types of resources, the project res directory contains specific subfolders:
Aplicatiile utilizeaza siruri de caractere, bitmap-uri, fisiere video si audio pentru a imbogati experienta utilizatorului. Toate aceste reprezinta resurse ale aplicatiei. In ciuda faptului ca sunt controlate din codul sursa, acestea reprezinta resurse externe si recomandarea este sa fie externalizate (nu le definitm in codul aplicatiei; de exemplu un string constant), pentru a facilita:
- internationalizarea interfetei cu utilizatorul prin furnizarea acesteia in diferite limbi si formate;
- portabilitatea aplicatiei pe dispozitive diferite.
Resursele sunt stocate in directorul res din cadrul proiectului Android. Pentru a gestiona diferite tipuri de resurse, directorul res al proiectului contine subdirectoare specifice:
Director | Resursa |
res/animator/ | fisiere XML ce definesc proprietatile animatiilor (property animations) |
res/anim/ | fisiere XML pentru animatii bazate pe transformari (tween animations) |
res/color/ | fisiere XML ce definesc culori si color state list resource |
res/drawable/ | Fisiere bitmap (.png, .jpg, .gif) sau fisiere XML ce definesc resurse ce pot fi desenate (drawable resource) |
res/layout/ | fisiere XML ce definesc design-ul interfetei (interface layout) |
res/menu/ | fisiere XML ce definesc meniurile aplicatiei (application menus) |
res/raw/ | fisiere diferite in format binar sau text |
res/values/ | fisiere XML care contin valori simple, cum ar fi siruri de caractere, numere intregi si culori. Este recomandat sa utilizati conventiile de nume de fisiere pentru anumite tipuri de resurse: arrays.xml (typed arrays), colors.xml (color values), dimens.xml (dimension values), strings.xml (string values), styles.xml (styles) |
res/xml/ | fisiere XML care pot fi citite Resources.getXML() |
Toate resursele din subdirectoarele directorului res/ sunt incluse in pachetul aplicatiei de catre compilator. Pentru a facilita accesul si controlul resurselor din codul aplicatiei, compilatorul genereaza o clasa, numita R, care contine identificatori statici utilizati pentru referirea fiecarei resurse.
- Important!
- Deoarece fisierele din resurse sunt compilate, NU salvati fisiere direct in interiorul directorului res/, deoarece aceasta va cauza o eroare de compilare.
De exemplu, este recomandat sa se defineasca si sa se salveze valori constante de tip String in interiorul fisierului res/values/strings.xml si sa nu se defineasca direct in cod. Fiind definite in afara codului sursa inseamna ca le puteti modifica fara a afecta codul sursa Java. Fisierul strings.xml poate arata ca acesta:
<!--?xml version="1.0" encoding="utf-8"?--> Hello World, Activity! Android
Asa cum am mai spus, pentru fiecare resursa compilatorul genereaza un identificator static si unic in clasa R. Identificatorul static are acelasi nume ca si elementul din strings.xml.
package itcsolutions.eu; public final class R { //... public static final class string { //unique ID for app_name string in strings.xml public static final int app_name=0x7f040001; //unique ID for hello string in strings.xml public static final int hello=0x7f040000; } }
Deci, daca se doreste preluarea valorii elementului hello (din strings.xml) intr-o variabila de tip String, se va folosi referinta statica din clasa R:
String stringHello = this.getString(R.string.hello);
Alte subiecte care fac parte din acest tutorial Android sunt accesibile prin intermediul articolului Tutorial Android – Descriere si cuprins.
Daca ai probleme cu exemplele sau crezi ca nu ai inteles elementele descrise, pune o intrebare in zona de comentarii si iti vom raspunde imediat. De asemenea, orice sugestie sau obervatie care duce la imbunatatirea materialului este bine venita.
Daca ti-a placut sau ti-a fost util acest tutorial atunci spune-le si altora despre el sau arunca-ti o privire pe reclamele din aceasta pagina. Referirea acestui material este cel mai bun mod de a aprecia autorul.
Salut, am si eu o intrebare.
Daca nu scri codul bine in fisierul XML, cum poti verifica ca ai gresit?
Foarte fain structurata teorie. Chiar a ajutat mult la intelegerea construirii unei aplicatii pt Android.
In Eclipse, in partea de jos ai fereastra “Console”. Daca nu o vezi, te duci ep Window->Show View->Consloe. Eroare va fi semnalata cam asa: ” Error in an XML file: aborting build.” Plus ca in fisierele XML iti apare acea bulina in stanga pe bara verticala, la fel ca in fisierele .java