Mihalcea Marius -Cătălin [607216]

1
Universitatea din Craiova
Facultatea de Științe

Lucrare de Licență

Îndrumător științific:
Dr. Prof. Asist. Irina Tudor

Absolvent: [anonimizat]
2016

2
Universitatea din Craiova
Facultatea de Științe

Dezvoltarea aplicațiilor Android
folosind AIDE

Îndrumător științific:
Dr. Prof. Asist. Irina Tudor

Absolvent: [anonimizat]
2016

Mihalcea Marius -Cătălin

Cuprins

1.Introducere ………………………….. ………………………….. ………………………….. ………………. 4
1.1.Despre Android ………………………….. ………………………….. ………………………….. …… 5
1.1.1.Istoric ………………………….. ………………………….. ………………………….. …………… 5
1.1.2.Caracteristici -cheie ………………………….. ………………………….. …………………….. 6
1.1.3.Hardware ………………………….. ………………………….. ………………………….. ……… 9
1.2.Despre AIDE ………………………….. ………………………….. ………………………….. …….. 10
2.Dezvoltarea de software ………………………….. ………………………….. ……………………….. 10
2.1.Fundamentele unei aplicații Android ………………………….. ………………………….. …. 12
2.2.Componentele unei aplicații Android ………………………….. ………………………….. …. 13
2.2.1.Activități ………………………….. ………………………….. ………………………….. ……… 13
2.2.2.Servicii ………………………….. ………………………….. ………………………….. ……….. 25
2.2.3.Furnizorii de conținut (Content pr oviders) ………………………….. …………………. 38
2.3.Fișierul Manifest ………………………….. ………………………….. ………………………….. … 44
2.4.Re sursele aplicației ………………………….. ………………………….. ………………………… 46
3.Note – aplicație Android pentru gestionarea de note ………………………….. ……………… 51
4.Bibliografie ………………………….. ………………………….. ………………………….. ……………… 58

4

1.Introducere
În prezent, lumea este din ce în ce mai conectată prin prisma evoluției tehnologiei
informației, aceasta arătând cum viețile oamenilor se schimbă. O ramură foarte
importantă a tehnologiei informației o constituie tehnologia mobilelor (Mobile
Technology), d eoarece dispozitivele mobile au început să ia locul PC -urilor, mai ales în
când facem referire la navigarea pe Internet. De asemenea, în viitor tehnologia mobilă va
avea un rol și mai important în viețile oamenilor. De exemplu, o să înlocuiască cardurile
bancare, o să înlocuiască camera foto digitală sau player -ul video pentru a reda conținut
video.
De asemenea, tehnologia mobilă a îmbunătățit viețile oamenilor din multe puncte
de vedere. În primul rând , apropie oamenii prin posibilitatea de a trimite/prim i mesaje
scrise, prin aplicațiile de socializare, prin apelare video sau vocală și prin alte activități
sociale. Oamenii pot alege cele mai potrivite și cele mai confortabile moduri pentru a
contacta prietenii și familia. În al doilea rând, tehnologia mobi la îi ajută pe oameni să se
relaxeze în timpul liber prin oferirea de nenumărate variante de divertisment. Spre
deosebire de telefoanele din anii ’90, telefoanele din ziua de azi pot realiza mai mult decât
un simplu apel telefonic. Oamenii pot să îl folose ască pentru a naviga pe Internet, juca
jocuri, citi cărți, captura fotografii și video, etc.
Cu toate acestea, din alt punct de vedere, dezvoltarea tehnologiei mobile are și
aspecte negative. Deși dispozitivele mobile aduc comoditate în viața de zi cu zi a
oamenilor, aceasta influențează viețile oamenilor și în mod negativ, atât din punct de
vedere fizic, cât și mintal, atunci când vine vorba de utilizarea îndelungată a acestor
dispozitive. În primul rând, deoarece mobilele oferă atât de multă comoditate, multe
persoane devin prea leneșe să facă lucruri prin propriile eforturi, cum ar fi să viziteze
prieteni, să cumpere lucruri din magazin, etc. Odată cu trecerea timpului, acest obicei se
va transforma în boli ale inimii, datorită lipsei de mișcare și a ra diației provenită din aceste
dispozitive. În al doilea rând, suprautilizarea dispozitivelor mobile dăunează oamenilor și
mintal. Odată cu dezvoltarea tehnologiei mobile, oamenii neglijează importanța
comunicării cu alții, deoarece se bazează prea mult pe d ispozitivele mobile. În trecut,

5
singura modalitate de a convorbi față în față era ca persoanele să se întâlnească în
persoană, dar astăzi același lucru se poate face printr -un apel video. De la suprafață,
pare că oamenii sunt mai conectați unul cu celălalt , dar adevărul nu este acesta. Cel mai
grav lucru este că oamenii nu mai gândesc singuri când apar probleme deoarece se pot
baza pe dispozitivele mobile, cu ajutorul cărora, cu câteva atingeri pot găsi răspunsuri la
orice întrebare. Deci, oamenii ar trebui să folosească tehnologia mobilă în mod moderat,
nu să se bazeze pe aceasta.
Concluzionând, tehnologia mobilelor are atât avantaje cât dezavantaje. Cea mai
importantă provocare este să fie folosită cum trebuie, pentru că doar folosind -o în mod
corespunzăt or va aduce beneficii și confort în viețile oamenilor.

1.1.Despre Android
Android este un sistem de operare pentru dispozitivele mobile, deținut de către
compania americană Google. Acesta este preinstalat pe o varietate de smartphone -uri
și tablete, de la diferiți producători, oferind acces la servicii Google ca Search, Youtube,
Maps, Gmail, etc. Aceasta înseamnă că un utilizator poate căuta informații pe web,
poate viziona videoclipuri, scrie e -mailuri sau căuta direcții direct de pe telefon, însă
Android este mai mult de atât.
Dispozitivele care au instalat sistemul de operare Android sunt foarte
personalizabile, astfel încât să satisfacă nevoile și gusturile utilizatorilor , cu ajutorul
temelor , fundalurilor și a altor elemente care schimbă complet aspectul interfeței
dispozitivului . Se pot descărca aplicații care fac diverse lucruri, ca de exemplu
gestionarea contului bancar, aplicații pentru socializare, jocuri, etc. Există zeci de mii de
aplicații și jocur i disponibile pentru descărcare din magazinul Google (Google Play store).

1.1.1. Istoric

Android, Inc. A fost fondat în Palo Alto, California în anul 2003 de către Andy
Rubin, Rich Miner, Nick Sears, și Chris White, pentru a de zvolta, după spusele lui Ru bin
”dispozitive mobile mai deștepte care sunt conștiente de locația și preferințele

6
utilizatorului”. Inițial, compania intenționa să dezvolte un sistem de operare avansat
pentru camerele foto digitale, însă a deviat spre a produce un sistem de operare pen tru
dispozitivele mobile, care să rivalizeze cu Symbian și Microsoft Windows Mobile.
În anul 2005, Google a cumpărat Android Inc., iar angajați cheie precum Rubin,
Miner și White au rămas la companie după achiziție. La Google, echipa condusă de Rubin
a dez voltat o platformă pentru dispozitivele mobile bazată pe kernel -ul Linux. Se specula
că, prin achiziția Android, Google intenționa să intre pe piaț a dispozitivelor mobile, și, la
sfârșitul anului 2007, la consorțiul Open Handse t Alliance, la care erau prez ente companii
din domeniul tehnologiei, ca Google, producători de dispozitive mobile ca HTC, Sony și
Samsung și operatori de telefonie mobilă ca T -Mobile, Android a fost dezvăluit primul său
produs, o platformă pentru dispozitivele mobile bazată pe kernel -ul Linux. Primul telefon
disponibil comercial cu sistemul de operare Android a fost HTC Dream, în Octombrie
2008.
Din anul 2008, Android a primit numeroase modificări, care au îmbunătățit treptat
sistemul de operare, adăugând noi caracteristici și rezolvân d problemele din versiunile
anterioare. Fiecare lansare majoră este denumită în ordine alfabetică, după un desert. În
2010, Google a lansat seria de dispozitive Nexus – o serie de smartphone -uri și tablete
care utilizau sistemul de operare Android, acestea fiind construite în colaborare cu
parteneri ca HTC, LG, Asus. Chiar de atunci, Google a realizat că viitorul dispozitivelor
mobile și viitorul web -ului o să se intersecteze. Probabil cel mai important lucru, atât
pentru Google și pentru utilizator, a fost că Android a promis să livreze metode pentru ca
serviciile și aplicațiile pot fi folosite și distribuite astfel. Cu toate că și alte companii știau
asta, doar Google a fost gata să creeze atât un sistem de operare, cât și să ofere servicii,
pentru a aduce Android în mâinile a câtor mai mulți utilizatori.

1.1.2. Caracteristici -cheie
Interfața sistemului de operare Android se bazează pe manipulare directă,
folosind gesturi cu degetul care corespund foarte mult cu acțiuni din lumea reală, ca
glisarea cu deg etul pe ecran pentru a manipula obiectele de pe ecran, împreună cu o
tastatură virtuală. Răspunsurile la atingerea utilizatorului sunt făcute să fie imediate și

7
oferă o interfață tactilă fluidă, adesea folosind capacitățile de vibrare ale dispozitivului
pentru a oferi atât un răspuns acustic, cât și unul tactil utilizatorului . Componente interne
ca accelerometre, giroscoape și senzori de proximitate sunt folosite de diverse aplicații
pentru a răspunde acțiunilor utilizatorului, ca de exemplu ajustarea ecran ului din modul
portret în modul vedere în funcție de orientarea dispozitivului, sau simularea controlului
unui volan în jocurile video.
Ecranul de start al unui dispozitiv Android conține de obicei icoane și widget -uri;
icoanele aplicațiilor pun în lansare aplicația respectivă, pe când widget -urile afișează
conținut ce se actualizează în timp real, cum ar fi prognoza meteo sau știri direct pe
ecranul de start. Un ecran de start poate conține mai multe pagini, între care utilizatorul
poate glisa înainte și î napoi, deși interfața ecranului de start este foarte personalizabilă ,
permițând utilizatorului să schimbe felul în care este afișat acesta. Cei mai mulți
producători personalizează dispozitivele Android pentru a se diferenția de competiție.
Aplicațiile car e gestionează interacțiune a cu ecranul de start se numesc , deoarece
acestea, printre altele, lansează aplicațiile instalate pe dispozitiv.
În partea de sus a ecranului se găsește o bară de stare, ce afișează informații
despre dispozitiv și conectivitate. Această bară poate fi extinsă pentru a afișa o listă de
notificări unde se găsesc informații importante sau actualizări de la aplicații, cum ar fi e –
mailurile noi sau mesajele, într -un mod care nu întrerup sau deranjează utilizatorul .
Notificările sunt viz ibile până în momentul citirii acestora prin atingere, lucru ce duce la
deschiderea aplicației respective. Începând cu Android 4.1, notificările extinse pot afișa
detalii suplimentare sau pot adăuga funcționalități. De exemplu, un player pentru muzică
poate afișa controale de redare, și o notificare pentru un apel pierdut oferă butoane pentru
a suna înapoi sau pentru a trimite un SMS.
Aplicațiile , care extind funcționalitatea dispozitivelor, sunt scrise folosind kit -ul de
dezvoltare software Android (SDK) ș i, de cele mai multe ori limbajul de programare Java
are acces complet la API -urile Android. Java poate fi combinat cu C/C++, împreună cu o
serie de runtime -uri diferite față de cele implicite care oferă un suport mai bun pentru
C++. Limbajul de programare Go este și el suportat , care poate fi folosit exclusiv
împreună cu o serie de API -uri restricționate Android. SDK -ul conține un set cuprinzător
de unelte de dezvoltare, incluzând un depanator (debugger), librării software, un emulator
bazat pe QEMU, docum entație, exemple de cod, și tutoriale.
Google a dezvoltat o secțiune de aplicații terț, care pot fi achiziționate de utilizatori
prin descărcarea și instalarea pachetului de aplicație Android (APK -ului), sau prin

8
descărcarea acestora folosind un magazin d e aplicații care permite instalarea,
actualizarea și ștergerea aplicațiilor. Google Play Store este magazinul de aplicații implicit
instalat pe dispozitivele Android care se conformează cu cerințele de compatibilitate
Google și licențele serviciilor mobile Google. Google Play Store permite utilizatorilor să
caute, să descarce și să actualizeze aplicații publicate de Google și dezvoltatori terț.
Din pricina naturii deschise a sistemului de operare Android, un număr de
magazine pentru aplicații terț există, fie pentru a oferi un înlocuitor pentru dispozitive ce
nu au voie să fie trimise cu Google Play Store, pentru a pune la dispoziție aplicații ce nu
pot fi oferite de Google Play Store, sau din alte motive.

Fig.1 – Arhitectura unui sistem Android (” By Smieh – Anatomy Physiology of an
Android, CC BY -SA 3.0, https://commons.wikimedia.org/w/index.php?curid=20067152 ”)

Gestionarea memoriei . Deoarece dispozitivele Android folosesc de obicei baterii,
Android este făcut să gestioneze procese și să mențină consumarea bateriei la minimum.
Când o aplicație nu este folosită, sistemul îi suspendă operațiile astfel încât, deși este

9
disponibilă pentru folosire imediată decât închisă complet, aceasta nu folosește baterie
sau resursele procesorului.
Android gestionează aplicațiile stocate în memorie automat , când memoria este
redusă, sistemul închizând procesele inactive, pornind de la procesele care au fost
inactive pe perioada cea mai mare. O analiză a arătat că aplicațiile terț care închid
procesele pentru a elibera memoria fac mai mult rău decât bine.

1.1.3. Hardware
Princi pala platformă hardware pentru Android este ARM (arhitecturile ARMx7 și
ARMv8), cu arhitecturile x86 și MIPS suportate oficial în versiunile mai recente de
Android. Începând cu Android 5.0 “Lollipop”, variantele pe 64 de biți ale tuturor
platformelor sunt supor tate. Proiectul neoficial Android -x86 a oferit suport pentru
arhitecturile x86 înaintea suportului oficial. Arhitectura MIPS a fost, de asemenea
suportată înaintea celor de le Google. Începând cu anul 2012, au început să se lanseze
dispozitive echipate cu procesoare Intel.
Cerințele pentru valoare minimă de memorie RAM pentru dispozitivele care
rulează Android variază de la 512 MB pentru ecrane cu densitate normală, la aproximativ
1.8 MB pentru ecranele cu densitate înaltă. Android 4.4 necesită un procesor cu
arhitectură ARMv7 pe 32 de biți, MIPS sau un procesor cu arhitectură x86. Android
suportă OpenGL ES 1.1, 2.0, 3.0 și 3.1. Unele aplicații pot necesita explicit o versiune de
OpenGL ES și o unitate de procesare grafică (GPU) potrivită pentru a putea rul a.
Dispozitivele Android încorporează multe componente hardware opționale,
precum camere foto/video, GPS, senzori de orientare, accelerometre, giroscoape,
barometre, senzori de proximitate, senzori de presiune, etc. Unele componente hardware
nu sunt neces are, dar au devenit standarde în anumite clase de dispozitive, precum
smartphone -urile, și necesități suplimentare sunt aplicate dacă aceste dispozitive sunt
prezente. Alte componente au fost inițial necesare, dar aceste necesități au devenit mai
puțin str icte, sau au dispărut cu totul. De exemplu, pe când Android era dezvoltat ca
sistem de operare pentru telefon, componente hardware ca microfonul erau o necesitate,
în timp funcția de telefon devenind opțională.
În afară de smartphone -uri și tablete, câțiv a furnizori rulează Android pe PC -uri
normale, și, pe lângă disponibilitatea lor pe hardware disponibil comercial, versiuni de

10
Android ce rulează pe PC sunt disponibile gratis din proiectul Android -x86, incluzând o
versiune personalizată de Android 4.4. Fo losind emulatorul Android disponibil în SDK,
sau folosind BlueStacks sau Andy, Android poate, de asemenea să ruleze non -nativ pe
arhitectura x86.

1.2.Despre AIDE
AIDE este un mediu de dezvoltare integrat (IDE), ce permite dezvoltarea de
aplicații Android bazate pe limbajul de programare Java și limbajul de marcare XML,
folosind SDK -ul Android. Aplicația AIDE are în componența sa o versiune mobilă a SDK –
ului Android, deci nu mai este nevoie de nici o instalare suplimentară. Prin intermediul
acestui mediu d e dezvoltare se pot crea aplicații native foarte performante, deoarece
acesta vine cu suport total pentru NDK -ul standard Android și include șabloane și
eșantioane.
Per total, AIDE a trecut printr -o serie de modificări de -a lungul timpului, cu noi
icoane, aspect mai plăcut și numerotarea liniilor. Schimbarea de file este mult mai fluidă
mulțumită unui gest nou de swipe și a unei interfețe bazată pe tab -uri.
Printre îmbunătățirile importante se numără și suportul pentru Git. Această
actualizarea oferă abil itatea de a crea automat arhive (repositories) cu noi proiecte și
efectua operații ca: crearea, unirea sau ștergerea ramurilor. Dacă luăm în considerare
faptul că versiunii originale de AIDE îi lipsea orice fel de control al surselor, și a câștigat
apoi un minim suport pentru Git, este un important pas în față. Capacitățile solide de
gestionare a versiunilor sunt foarte importante în folosirea eficientă a acestui mediu de
dezvoltare în proiecte mai serioase unde controlul surselor este o necesitate.

2.Dezvoltarea de software
Sistemul de operare Android este dezvoltat în mod privat de Google, până când
ultimele modificări și actualizări sunt gata să fie lansate, moment în care codul sursă este
disponibil în mod public. Codul sursă o să ruleze nemodifica t pe dispozitive selectate, de
obicei seria Nexus. Codul sursă Android nu conține obișnuitele drivere de dispozitive care
sunt necesare anumitor componente hardware.

11
Google oferă actualizări majore pentru Android la fiecare șase sau nouă luni, ce
au nume cu o tematică din cofetărie . Cea mai recentă versiune de Android este 6.0
“Marshmallow ”. De obicei , actualizările Android, exceptând dispozitivele din gama Nexus,
ajung la câteva luni după lansarea unei noi versiuni, sau chiar deloc. Acest lucru se
datore ază atât diversității pe partea de hardware a dispozitivelor Android, cât și prin
prisma faptului că codul sursă oficial rulează doar pe dispozitivele proprii Google, și
anume gama Nexus. Adaptarea sistemului de operare Android la un hardware specific
este un proces ce consumă timp și resurse pentru producătorii de dispozitive mobile, care
prioritizează dispozitivele nou -apărute și de obicei le lasă pe cele mai vechi în urmă.
Astfel, dispozitivele mai vechi nu sunt de obicei actualizate dacă producătorul de cide că
această procedură nu merită investiția de resurse, deși dispozitivul poate fi compatibil.
Problema se agravează atunci când producătorii personalizează Android cu propriile
interfețe și aplicații, care trebuie reaplicate pentru fiecare versiune.
Lipsa de suport după vânzare din partea producătorilor a fost criticată de grupuri
de consumatori și de presă. Din această cauză Google, în parteneriat cu un număr de
jucători din industrie au anunțat Alianța Actualizărilor Android ( “Android Update Alliance ”),
ce își propunea să ofere actualizări pentru fiecare dispozitiv pentru 18 luni după lansarea
acestuia, însă nu a mai fost nici un cuvânt oficial de când această alianță s -a înființat.
În anul 2012, Google a decis să facă actualizările mai ușoare pentru aplicațiile de
bază, punând la dispoziție actualizarea acestora prin Google Play Store, indiferent de
sistemul de operare. Una dintre componente, Google Play Services, este un proces la
nivel de sistem, care oferă API -uri pentru serviciile Google, instala te automat pe toate
dispozitivele ce rulează Android începând cu versiunea 2.2. Cu aceste schimbări, Google
poate să adauge funcționalitățile unui sistem de operare nou prin Google Play Services
și actualizări de aplicații, fără a mai fi nevoie de distribu irea și reinstalarea sistemului de
operare.
Nucleul sistemului de operare Android este bazat pe nucleul cu suport pe durata
lungă (LTS) al Linux. Dispozitivele Android folosesc, în mare parte versiunile 3.4 sau 3.10
ale nucleului Linux. Android a folosit de-a lungul timpului diverse versiuni, începând cu
versiunea 2.6.25, folosită în Android 1.0. Versiunea Android a nucleului Linux are
schimbări arhitecturale implementate de Google în afara ciclului de dezvoltare al unui
nucleu Linux tipic, ca includerea u nor componente ca Binder, ashmem, pmem, și diverse
aut-of-memory (OOM).

12
2.1.Fundamentele unei aplicații Android
Aplicațiile Android sunt scrise în limbajul de programare Java. Uneltele din SDK –
ul Android compilează codul, împreună cu orice dată și fișier resursă într -un APK (Android
package), care este un fișier arhivă cu sufixul “.apk”. Un fișier APK conține toate
componentele unei aplicații Android și este fișierul care se folosește pentru a ins tala
aplicația pe un dispozitiv Android.
Odată instalată pe dispozitiv, fiecare aplicație Android “trăiește ” în propria cutie de
securitate:
 Sistemul de operare Android este un sistem Linux cu mai mulți utilizatori în care
fiecare aplicație este un utiliz ator diferit .
 În mod implicit, sistemul atribuie fiecărei aplicații un ID Linux de utilizator unic
(acesta este folosit de sistem și este necunoscut pentru aplicație). Sistemul
setează permisiuni pentru toate fișierele din aplicație, în așa fel încât numai ID-
ul utilizatorului atribuit acelei aplicații poate să le acceseze.
 Fiecare proces are propria mașină virtuală, astfel încât codul unei aplicații să
ruleze izolat față de alte aplicații.
 În mod implicit, fiecare aplicație rulează în propriul proces Linux. Android
pornește procesul atunci când oricare dintre componentele aplicației trebuie
executată, iar apoi închide procesul când nu mai este nevie de acesta sau cân
sistemul trebuie să recu pereze memorie pentru alte aplicații.
În acest fel, sistemul de operare Android implementează “principiul celui mai mic
privilegiu ”. Aceasta înseamnă că fiecare aplicație, în mod implicit, are acces la
componenta de care este nevoie pentru a realiza obiect ivele, nimic mai mult. Astfel se
creează un mediu foarte sigur, în care o aplicație nu poate accesa părți ale sistemului
asupra cărora nu are permisiune.
Totuși, există modalități pentru o aplicație să partajeze date cu alte aplicații sau să
acceseze serv icii din sistem:
 Este posibil ca două aplicații să împartă același ID utilizator de Linux, caz în
care acestea își pot accesa fișierele. Pentru a conserva resurse de sistem,
aplicațiile cu același ID utilizator pot să ruleze același proces Linux și să
împa rtă aceeași mașină virtuală.

13
 O aplicație poate să solicite date de pe dispozitiv, cum ar fi contactele
utilizatorului, mesajele SMS, camera, cardul SD, Bluetooth și altele. Utilizatorul
trebuie să acorde explicit aceste permisiuni.

2.2.Componentele unei a plicații Android
Componentele aplicațiilor sunt esențiale pentru a construi module ale unei aplicații
Android. Fiecare componentă este un punct diferit prin care sistemul poate să acceseze
aplicația. Nu toate componentele sunt puncte de intrare pentru uti lizator și unele depind
unele de altele, dar fiecare există ca o entitate și joacă un rol specific – fiecare este un
modul unic, care ajută în definirea comportamentului general al aplicației.
Există patru tipuri diferite de componente. Fiecare tip are un rol diferit și un ciclu
de viață diferit care definește cum est e creată și distrusă componenta.

2.2.1. Activități

O activitate reprezintă un singur ecran cu o interfață utilizator. De exemplu, o
aplicație de e -mail poate să aibă o activitate care afiș ează o listă de e -mailuri noi, altă
activitate care să permită crearea unui e -mail, și alta pentru a citi e -mailuri. Cu toate că
activitățile cooperează pentru a furniza o experiență de utilizare corelată, fiecare este
independentă de cealaltă. De exemplu, o altă aplicație poate porni oricare din aceste
activități (dacă aplicația curentă permite acest lucru). De exemplu, o aplicație pentru
cameră poate să pornească activitatea din aplicația de e -mail care se ocupă cu crearea
unui noi e -mail, pentru a permit e utilizatorului să partajeze o fotografie.
De obicei, o aplicație este constituită din mai multe activități care interacționează.
Una dintre aceste activități este desemnată drept activitate principală ( “main” ), care
întâmpină utilizatorul atunci când acesta lansează pentru prima dată. Fiecare activitate
poate începe apoi o altă activitate pentru a realiza diverse acțiuni. De fiecare dată când
se execută o nouă activitate, precedenta este oprită, însă este păstrată într -o stivă ( “back
stack” ). Această s tivă implementează principiul simplu ”last in, first out”, aceasta
însemnând că, atunci când utilizatorul nu mai are nevoie de activitatea curentă, și acesta
apasă butonul „înapoi”, activitatea este scoasă din stivă și precedenta activitate se reia.

14
Când o activitate se oprește pentru ca altă activitate să înceapă, este notificată de
această schimbare de stare prin intermediul ciclului de viață a activității. Există câteva
metode pe care o activitate le poate primi, datorită schimbării de stare – fie prin prisma
faptului că sistemul a creat -o, a oprit -o, a reluat -o sau a distrus -o – și fiecare metodă oferă
oportunitatea de a realiza obiective care au legătură cu schimbarea stării. De exemplu,
când este oprită, o activitate trebuie să distrugă orice obiect m are, cum ar fi conexiuni la
rețea sau la baze de date. Când activitatea își reia cursul, se pot procura resursele
necesare și acțiunile care au fost întrerupte se pot relua. Aceste tranziții de stare face
parte din ciclul de viață al unei activități.
Pent ru a crea o activitate , trebuie mai întâi creată o subclasă a clasei “Activity” .
În această subclasă trebuie implementate metodele care sunt apelate de sistem când
activitatea trece dintr -o stare în alta a ciclului de viață, cum ar fi cazul în care activit atea
este creată, oprită, reluată, sau distrusă. Cele mai importante metode sunt:
 onCeate() – această metodă trebuie implementată. Sistemul apelează această
metodă când o activitate este creată. Mai important, aici trebuie apelată metoda
“setContentView()” pentru a defini așezarea pentru interfața cu utilizatorul
 onPause() – sistemul apelează această metodă la prima indicație a faptului că
utilizatorul părăsește activitatea (aceasta nu înseamnă întotdeauna că activitatea
este distrusă). Aici este locul în care se comit orice modificări care ar trebui
păstrate după ce utilizatorul închide sesiunea.
Există și alte metode ale ciclului de viață al unei activități care trebuie folosite
pentru a oferi o experiență fluidă între activități și a gestiona întreruperi neașteptate care
rezultă în oprirea și distrugerea activității.
Interfața cu utilizatorul pentru o activitate este oferită de o ierarhie de vederi
(“views” ), obiecte derivate din clasa “View”. Fiecare view controlează un spațiu
dreptunghiular din fereast ra activității și poate răspunde la interacțiunea utilizatorului . De
exemplu , un view poate fi un buton care inițiază o acțiune când utilizatorul îl apasă.
Android oferă un număr de vederi gata-făcute pentru a putea fi folosite pentru a
proiecta și organi za așezarea (”layout”). “Widget -urile” sunt veder i care oferă ele mente
vizuale pentru ecran, cum ar fi butoane, câmpuri text, sau imagini. “Layout -urile” sunt
vederi derivate din clasa “ViewGroup” pentru a crea widget -uri și layout -uri proprii și
pentru a le aplica în layout -ul activității.
Cea mai folosită modalitate pentru a defini un layout folosind vederi este un fișier
XML salvat în resursele aplicației. În acest fel, aspectul interfeței c u utilizatorul poate fi

15
separat de codul sursă care definește c omportamentul activității. Un layout se poate seta
ca interfață utilizator pentru activitatea curentă folosind metoda ”setContentView()”,
utilizând ca parametru pentru aceasta ID -ul resursei pentru layout. Mai mult, se pot crea
vederi noi în codul activită ții și se poate crea o ierarhie de vederi prin inserarea de vederi
într-un ”ViewGroup”, folosind apoi ace l layout prin trimiterea acestui grup ca parametru
al metodei ”setContentView()”.
Activitatea trebuie declarată într -un fișier ”manifest” pentru a fi accesibilă din
sistem. Pentru a declara o activitate, este necesară deschiderea fișierului ”manifest” și
adăugarea unui element ” <activity> ” ca și copil al elementului ” <application> ”. De
exemplu:
<manifest … >
<application … >
<activity android:name =".ExempluActivitate " />

</application … >

</manifest >
Există mai multe atribute care pot fi incluse în acest element, pentru a defini
proprietăți cum ar fi eticheta activității, o icoană pentru activitate, sau o t emă pentru
interfața activității cu utilizatorul . Atributu l ”android:name” este singurul a tribut necesar –
acesta specifică numele clasei activității. Odată ce o aplicație este publicată, acest nume
nu ar trebui schimbat, pentru că schimbarea acestuia poat e afecta funcționalitatea, cum
ar fi comenzile rapide ale aplicației.
Un element ” <activity> ” poate, de altfel specifica numeroase ” filtre de intenție ”,
folosind elementul ” <intent -filter > ” pentru a declara cum alte aplicații o pot activa.
Când se creea ză o nouă aplicație folosind SDK -ul Android, activitatea -părinte care
este creată automat conține un filtru de intenție prin care se specifică faptul că activitatea
răspunde la acțiunile ”main” și ar trebui plasată în categoria ”launcher”. Filtrul de inten ție
arată în felul următor:
<activity android:name =".ActivitateExemplu" android:icon ="@drawable/icoana_app " >
<intent-filter>
<action android:name ="android.intent.action.MAIN" />
<category android:name ="android.intent.category.LAUNCHER" />
</intent -filter>
</activity>

16
Elementul ” <action> ” specifică faptul că acesta este principalul punct de intrare în
aplicație. Elementul ” <category> ” specifică faptul că această activitate ar trebuie listată în
launcher -ele aplicațiilor din sistem ( pentru a permite utilizatorilor lansarea aplicației).
Dacă nu se dorește faptul ca aplicația să permită altor aplicații să îi activeze
activitățile, să fie de sine stătătoare, atunci nu este necesară declararea altor filtre de
intenție. Doar o activitate ar trebui să aibă acțiunea ”main” și categoria ”launcher”, ca și
în exemplul anterior. Activitățile care nu trebuie să fie disponibile altor aplicații nu trebuie
să aibă nici un filtru de intenție și pot fi puse în execuție folosind intenții explicite.
Cu toate acestea, dacă se dorește ca aplicația să răspundă la intenții implicite care
sunt furnizate de alte aplicații, atunci filtre de intenție adiționale trebuie definite. Pentru
fiecare tip de intenție pentru care aplicația trebuie să răspundă , un elemen t ”<intent –
filter>” trebuie inclus . Acesta include un element ” <action>” și opțional un element
”<category>” sau un element ” <data>”. Aceste elemente specifică tipul de intenție la care
aplicația poate să răspundă.
Executarea unei activități se poate realiza prin apelarea metodei ”startActivity()”,
ce primește ca parametru un ”Intent” care descrie activitatea care urmează a fi executată.
Intenția specifică fie activitatea exactă care se dorește a fi executată, fie tipul acțiunii ce
urmează a fi executa tă (și sistemul selectează activitatea potrivită, care poate fi chiar din
o aplicație diferită). O intenție poate, de altfel transporta mici cantități de date pentru a
putea fi folosite de aplicația ce urmează a fi pornită.
O activitate se poate lansa pri n crearea unei intenții care definește explicit ce
activitate se dorește a fi executată, folosind numele clasei. În exemplul de mai jos, o
activitate pornește o altă activitate numită ”ExampleActivity”:

Intent intent = new Intent(this, ExampleActivity .class);
startActivity (intent);
O aplicație poate să execute anumite acțiuni, ca de exemplu trimiterea unui email,
SMS, sau o actualizare de stare. În acest caz, este posibil ca aplicația să nu aibă activități
proprii pentru a executa asemenea acțiuni, deci o soluție este ca aplicația să ”împrumute”
activități oferite de alte aplicații aflate pe dispozitiv, care pot executa astfel de acțiuni. Aici
se arată adevărata valoare a intențiilor – se poate crea o intenție care să descrie o acțiune
care se dorește a fi executată și sistemul lansează activitatea potrivită din altă aplicație.
Dacă sunt mai multe activități care pot să satisfacă intenția, atunci utilizatorul poate

17
selecta dintre acestea una. De exemplu pentru ca utilizatorul să poată trimite un email se
poate crea următoarea intenție:
Intent intent = new Intent(Intent.ACTION_SEND );
intent.putExtra (Intent.EXTRA_EMAIL ,vectorDestinatari );
startActivity (intent);
Informația adăugată în plus la intenție, ”EXTRA_EMAIL” este un vector de obiecte
String , fiecare dintre acestea conținând numele unui destinatar către care va fi trimis
emailul. Când o aplicație pentru email răspunde la această intenție, citește vectorul din
datele extra ale intenției și le adaugă în câmpul ”către” al formularului de creare email. În
această situație , activitatea aplicației pentru email se execută și când utilizatorul termină,
activitatea curentă este reluată.
În anumite cazuri, este necesară întoarcerea unui rezultat din partea activității care
a fost pornită. În acest caz, activitatea se execută folosind metoda
”startActivityForResult() ”. Pentru a primi rezultatul de la o activitate ulterioară, este
necesară implementarea metodei ” onActivityResult() ”. Când activitatea ulterioară este
terminată, aceasta returnează rezultatul într-un obiect de tip ”Intent” în cadrul aceleiași
metode.
De exemplu, poate că se dorește ca utilizatorul să aleagă unul dintre contacte,
pentru ca activitatea să proceseze informația din acel contact. Mai jos este un exemplu
despre cum se poate crea o astfel de intenție care să gestioneze rezultatul:
private void preiaContact () {
//Se crază o intenție pentru a prelua un contact, cum sunt definite de URI -ul
furnizorului
Intent intent = new Intent(Intent.ACTIUNE_PRELUARE , Contacts .URI_CONTINUT );
cautaActivitate (intent, CERERE_PRELUARE_CONTACT );
}

@Override
protected void onActivityResult (int codCerere , int codRezultat , Intent data) {
// Dacă cererea a decurs bine (OK) și a fost CERERE_PRELUARE_CONTACT
if (codRezultat == Activity .REZULTAT_OK && codCerere == CERERE_PRELUARE_CONTACT )
{
// Se execută o interogare către furnizorul conținutului contactelor pentru a
întoarce numele contactului
Cursor cursor = getContentResolver ().query(data.getData(),
new String[] {Contacts .NUME }, null, null, null);
if (cursor.moveToFirst ()) { // True if the cursor is not empty
int indexColoana = cursor.getColumnIndex (Contacts .NUME);

18
String nume = cursor.getString (indexColoana );
// Procesează numele contactului
}
}
}
În acest exemplu se prezintă logica de bază care ar trebui folosită în metoda
”onActivityResult” pentru a gestiona un rezultata t al unei activități. Prima condiție verifică
dacă cererea a fost realizată cu succes – dacă a fost, atunci ”codRezultat” va fi
”REZULTAT_OK” – și dacă cererea la care rezultatul răspunde este cunoscută – în acest
caz, ”codCerere” este egal cu al doilea par ametru trimis cu metoda
”startActivityForResult()”. De aici, codul gestionează rezultatul activității prin interogarea
datelor returnate într -un obiect ”Intent” (parametrul ”data”).
Închiderea unei activități se poate face prin apelarea metodei ”finish()” . O
activitate separată care a fost executată anterior se poate închide folosind metoda
”finishActivity()”. În cele mai multe cazuri, nu este necesară închiderea unei activități
folosind aceste metode. Sistemul de operare Android se ocupă de gestionarea ci clului de
viață al unei activități, în așa fel încât să nu mai fie necesară închiderea manuală a
activităților. Apelarea acestor metode poate afecta experiența utilizatorului și ar trebui
folosite când se dorește ca utilizatorul să se întoarcă la această i nstanță a activității.
Gestionarea ciclului de viață al unei activități prin apelarea de metode este
crucială în dezvoltarea de aplicații fiabile și flexibile. Ciclul de viață al unei activități este
afectat direct de asocierea lui cu alte activități, sarcini, și stiva din spate (”back stack”).
O activitate poate exista într -una din cele trei stări:
 Reluată (”Resumed”) – Activitatea este în prim -planul ecranului și are atenția
utilizatorului. (Această stare este de asemenea cunoscută ca ”în execuție”.)
 Întreruptă(”Paused”) – O altă activitate este în prim -plan și are atenție, d ar această
activitate este încă vizibilă. Aceasta înseamnă că o altă activitate este vizibilă
deasupra acestei activități și acea activitate este parțial transparentă sau nu
acoperă în totalitate ecranul. O activitate întreruptă este complet ”în viață”
(obiectul ”Activity” este reținut în memorie, el îșî menține toate stările și informațiile
membrilor, și rămâne atașat managerului de ferestre), dar poate fi distrusă de
sistem în cazurile în care memoria este foarte redusă.
 Oprită(”Stopped”) – Activitate es te complet ascunsă de o altă activitate (activitatea
se află acum în fundal – ”background”). O activitate oprită este, de asemenea ”în

19
viață” (obiectul ”Activity” este reținu t în memorie, el își menține toate stările și
informațiile membrilor, dar nu mai e ste atașat managerului de ferestre). În orice
caz, aceasta nu mai este vizibilă de către utilizator și poate fi distrusă de sistem
când este nevoie de memorie în altă parte.
Dacă o activitate este întreruptă sau oprită, sistemul poate să o elimine din
memo rie fie prin a -i cere oprirea (prin apelarea metodei ”finish()”), sau prin închiderea
procesului acesteia. Când activitatea este redeschisă, (după ce a fost oprită sau distrusă),
aceasta trebuie creată de la început.
Când o activitate realizează tranziții între diferitele stări pe care le poate avea, este
notificată prin numeroasele metode callback. Toate aceste metode sunt ”cârlige” care pot
fi suprascrise pentru a realiza obiectivele dorite atunci când se trece de le o stare la alta.
Următorul exemplu inc lude fiecare metodă fundamentală a ciclului de viață:
public class ActivitateExemplu extends Activity {
@Override
public void onCreate (Bundle stareSalvata ) {
super.onCreate (stareSalvata );
// Activitatea este creată.
}
@Override
protected void onStart() {
super.onStart();
//Activitatea este în pragul de a fi vizibilă .
}
@Override
protected void onResume () {
super.onResume ();
// Activitatea este vizibilă (este Reluată – ”Resumed”).
}
@Override
protected void onPause() {
super.onPause();
// Altă activitate preia focusul (această activitate este în pragul de
a fi Întreruptă –”Paused”).
}
@Override
protected void onStop() {
super.onStop();
// Activitatea nu mai este vizibilă (este Oprită – ”Paused”)
}
@Override
protected void onDestroy () {
super.onDestroy ();
//Activitatea urmează să fie distrusă.

20
}
}
Luate împreună, aceste metode definesc întregul ciclu de viață al unei activități.
Prin implementarea acestor metode, se pot monitoriza trei bucle imbricate în ciclul de
viață al unei activități:
 Întreaga durată de viață a unei activități are loc între apelarea metodei
”onCreate()” și apelarea metodei ”onDestroy()”. Activitatea ar trebui să realizeze
setarea unei stări globale (ca de exemplu definirea layout -ului) în metoda
”onCreate()” și să elibe reze toate resursele rămase în cadrul metodei
”onDestroy()”. De exemplu, dacă activitatea are un fix de execuție aflat în execuție
în fundal pentru a descărca date din rețea, este posibil ca aceasta să creeze firul
de execuție în metoda ”onCreate()” și să îl oprească în metoda ”onDestroy()”.
 Durata de viață vizibilă a unei activități are loc între apelarea metodei ”onStart()”
și apelarea metodei ”onStop()”. În acest timp, utilizatorul poate vedea activitatea
pe ecran și poate interacționa cu ea. De exemplu, metoda ”onStop()” este apelată
când o nouă activitate este pusă în execuție și această activitate nu mai este
vizibilă. Între aceste două metode, se poate înregistra un ”BroadcastReceiver” în
metoda ”onStart()” pentru a monitoriza schimbările care au impa ct asupra interfeței
cu utilizatorul și se poate anula această înregistrare în metoda ”onStop()” când
utilizatorul nu mai p oate vizualiza ce afișează act ivitatea. Sistemul poate apela
ambele metode de mai multe ori în timpul unui întreg ciclu de viată al u nei activități ,
în timp ce activitatea alternează între a fi vizibilă și ascunsă utilizatorului.
 Durata de viață din prim -plan a unei activități are loc între apelarea metodei
”onResume()” și apelarea metodei ”onPause()”. În acest timp activitatea este în
fața tuturor celorlalte activități de pe ecran și are focusul utilizatorului. O activitatea
poate tranzita frecvent din și în prim -plan – de exemplu, metoda ”onPause()” este
apelată când dispozitivul intră în stand -by sau când apare un dialog. Deoarece
această stare poate tranzita des, codul din aceste metode ar trebui să fie ”ușor”
pentru a evita tranziții lente care fac utilizatorul să aștepte.

În Figura 2 se ilustrează aceste bucle și căile pe car e o activitate poate să le ia
între stări. Dreptunghiurile reprezintă metodele callback care pot fi implementate pentru
a realiza acțiuni când activitatea tranzitează între stări.

21

Salvarea stării unei activități

Așa cum s -a menționat la începutul capitolului, atunci când o activitate este
întreruptă sau oprită, starea acesteia este reținută. Acest lucru este adevărat deoarece
obiectul ”Activity” este încă reținut în memorie când activitatea este întreruptă sau oprită
– toate informațiile despre mem brii acesteia și starea curentă sunt încă active. Prin
urmare, orice modificare pe care o face utilizatorul în activitate este reținută astfel încât
atunci când activitatea se întoarce în prim -plan, acele modificări sunt acolo.
În orice caz, când sistemul distruge o activitate pentru a recupera memorie,
obiectul ”Activity” este distrus, pentru ca sistemul să nu poată să o reia cu starea intactă.
În schimb, sistemul trebuie să recreeze obiectul dacă utilizatorul navighează înapoi la
acesta. Totuși, utilizato rul nu este conștient de faptul că sistemul a distrus activitatea și a
creat -o din nou și, prin urmare, probabil se așteaptă ca activitatea să fie exact cum era
înainte. În această situație, pentru ca informația importantă despre starea activității să fie
reținută este necesară implementarea unei metode adiționale care permite salvarea
informațiilor despre starea activității: ”onSaveInstanceState()”.
Sistemul apelează această metodă înainte de a face activitatea vulnerabilă la
distrugere. Sistemul trimite m etodei un obiect ”Bundle”, în care se pot salva informații
despre starea activității ca perechi nume -valoare, folosind metode ca ”putString()” și
”putInt()”. Mai apoi, dacă sistemul distruge procesul aplicației și utilizatorul navighează
înapoi la activita te, sistemul va recrea activitatea și va trimite obiectul ”Bundle” la
metodele ”onCreate()”, respectiv ”onRestoreInstanceState()”. Folosind una dintre aceste
metode, se poate extrage starea salvată din acest obiect și restaura starea activității.
Dacă nu e ste nicio informație despre stare, atunci obiectul trimis este null (este cazul în
care activitatea este creată pentru prima dată).

22

Fig.2 – Ciclul de viață al unei activități

23
Fig.3 – Restabilirea unei inst anțe, cele două moduri prin care o activitate se întoarce la
focusul utilizatorului când starea acesteia este intactă: fie activitatea este distrusă,
recreată ulterior și activitatea trebuie să restabilească starea salvată anterior, sau
activitatea este oprită, reluată ulterior și starea activității rămâne intactă.

Nu este nicio garanție că metoda ”onSaveInstanceState()” se va apela înainte de
distrugerea activității, pentru că există cazuri în care nu va fi necesară salvarea stării (de
exemplu când utilizatorul părăsește activitatea folosind buton ul ”Înapoi”, pentru că
utilizatorul a închis explicit activitatea). Dacă sistemul apelează această metodă, o va
face înainte de apelarea metodei ”onStop()” și ăpsibil înainte de ”onPause()”.
Cu toate acestea, chiar da că nu se implementează metoda
”onSaveI nstanceState()”, puțin din starea activității este restabilită de implementarea
implicită a metodei din clasa ”Activity”. Mai precis, implementarea implicită apelează
metoda corespunzătoare pentru fiecare vedere (”View”) din layout, ceea ce permite ca
fiecare vedere să ofere informații despre ea însăși ce trebuie salvate. Aproape fiecare
widget din Android implementează această metodă într -un mod adecvat, în așa fel încât
orice schimbare vizibilă la interfața cu utilizatorul sunt salvate automat și restabi lite când
activitatea este recreată. De exemplu, widget -ul ”EditText” salvează orice text adăugat
de utilizator și widget -ul ”CheckBox” salvează starea de bifat sau nu. Pentru a salva
starea unui widget, este necesară specificarea unui ID unic (folosind at ributul ”android:id”)
pentru fiecare widget a cărui stare se dorește a fi salvată. Dacă un widget nu are un ID,
atunci sistemul nu îi poate salva starea.
Deoarece implementarea implicită a metodei ”onSaveInstanceState()” ajută la
salvarea stării interfețe i cu utilizatorul, dacă se suprascrie metoda pentru a salva
informații adiționale despre stare, trebuie întotdeauna apelată implementarea superclasei
a acestei metode înainte de a prelucra informația. O metodă bună de a testa abilitatea
unei aplicații de a -și restabili starea este prin simpla rotire a dispozitivului astfel încât
orientarea ecranului să se schimbe. Când se întâmplă acest lucru, sistemul distruge și
recreează activitatea pentru a aplica resurse alternative care pot fi disponibile pentru
noua configurație a ecranului. Doar din acest motiv, este foarte important ca activitatea
să-și restabilească complet starea când este recreată, deoarece utilizatorii rotesc de
obicei ecranul atunci când folosesc aplicații.

Gestionarea schimbărilor de configurare

24

Configurările unor dispozitive se pot schimba la rulare (de exemplu orientarea
ecranului, disponibilitatea tastaturii și limba).Când o asemenea schimbare are loc,
Android recreează activitatea aflată în execuție (sistemul apelează ”onDestroy( )”, apoi
imediat ”onCreate()”). Acest comportament este proiectat astfel încât aplicația să se
adapteze le noile configurații prin reîncărcarea automată a aplicației cu resurse
alternative care sunt oferite (ca de exemplu, layout -uri diferite pentru diferi te orientări ale
ecranului și dimensiuni).
Dacă activitatea este proiectată corespunzător pentru a gestiona o repornire din
cauza schimbării orientării ecranului și aceasta restabilește starea activității, aplicația va
fi mai flexibilă la alte evenimente neașteptate d in ciclul de viață al acesteia. Cea mai bună
modalitate de a gestiona o asemenea repornire este să se salveze și restabilească starea
activității folosind metodele ”onSaveInstanceState()” și ”onRestoreInstanceState()”.

Coordonarea activităților

Când o activitate pune în execuție o altă activitate, ambele experimentează
tranzițiile din ciclul de viață. Prima activitate se întrerupe și se oprește(totuși, nu se va
opri dacă este încă vizibilă în fundal), în timp ce cealaltă activitat e este creată. În cazul în
care aceste activități împart date salvate pe disc sau altundeva, este important ca prima
activitate să nu fie complet oprită înainte ca ce -a de-a doua să fie creată. Mai degrabă,
procesul de a porni a doua activitate se suprapun e cu procesul de a opri prima activitate.
Ordinea apelării metodelor în ciclul de vi ață este bine definit, mai ales când cele
două activități se află în același proces și una este pornită de cealaltă. Aceasta este
ordinea operațiilor care apar când activi tatea A pune în execuție activitatea B:
1. Metoda ”onPause()” a activității A se execută
2. Metodele ”onCreate()”, ”onStart()” și ”onResume()” ale activității B se execută
secvențial. (Activitatea B are focusul utilizatorului.)
3. Dacă activitatea A nu mai este viz ibilă pe ecran, metoda ”onStop()” a acesteia
se execută
Această secvență predictibilă a apelării metodelor ciclului de viață permite
gestionarea tranzițiilor de informație de a o activitate la cealaltă. De exemplu, dacă trebuie
să se scrie în baza de date când prima activitate se oprește pentru ca următoarea

25
activitate să poat ă citi informațiile scrise, atu nci scrierea în baza de date ar trebui să se
facă în cadrul metodei ”onPause()”, în loc de ”onStop()”.

2.2.2. Servicii

Un serviciu este o componentă care rulează în fundal pentru a realiza operații de
lungă durată sau a realiza muncă pentru procesele de la distanță. Un serviciu nu oferă o
interfață utilizator. De exemplu, un serviciu poate reda muzică în fundal, în timp ce
utilizatorul folosește o aplic ație diferită, sau poate aduce date din rețea fără a bloca
interacțiunea utilizatorului cu o activitate. Altă componentă , cum ar fi o activitate poate să
pornească un serviciu și îl poate lăsa să ruleze, sau să se lege de acesta pentru a
interacționa cu ac esta.
Un serviciu poate avea două stări:
 Pornit (”Started ”) – un serviciu este pornit atunci când o componentă a aplicației
(de exemplu o activitate ) îl pune pe acesta în execuție prin apelarea metodei
”startService()”. Odată pornit, un serviciu poate rul a în fundal pe termen nelimitat,
chiar dacă componenta care l -a pornit este distrusă. De obicei, un serviciu pornit
realizează o singură operație și nu returnează un rezultat apelantului. De exemplu,
acesta poate să descarce un fișier prin rețea. Când acea stă operație se sfârșește,
serviciul ar trebui să se oprească singur.
 Legat (”Bound”) – un serviciu este ”legat” în momentul în care o componentă a
aplicației face legătura cu acesta prin intermediul metodei ”boundService()”. Un
serviciu legat oferă o interfață client -server care permite componentelor să
interacționeze cu serviciul, să trimită cereri, să primească rezultate . Un serviciu
legat rulează numai atât timp cât o altă componentă a aplicației este legată de
acesta. Componente multiple pot fi leg ate la serviciu o dată, dar când toate se
dezleagă, serviciul este distrus.

Deși aceste servicii sunt de două tipuri, aceasta nu înseamnă că un serviciu nu
poate fi atât pornit, și poate permite legarea. Este pur și simplu o chestiune de a
implementa dou ă metode: ”onStartCommand()” pentru a permite componentelor să
pornească serviciul și ”onBind()” pentru a permite legarea.
Indiferent dacă serviciul este pornit, legat, sau ambele, orice componentă a unei
aplicații poate utiliza serviciul (chiar și dintr -o aplicație separată), în același mod în care

26
o componentă poate să utilizeze o activitate – prin pornirea acesteia cu un ”Intent”. Cu
toate acestea, un serviciu se poate declara ca privat, în fișierul manifest, și bloca asfel
accesul din alte aplicații. U n serviciu rulează în firul de execuție principal al procesului
gazdă – serviciul nu își creează propriul fir de execuție și nu rulează într -un proces
separat (decât dacă se specifică acest lucru). Acest lucru înseamnă că, dacă serviciul va
solicita proces orul sau va bloca operații, ar trebui creat un nou fir de execuție în serviciu
care să realizeze acest lucru . Prin folosirea unui fir de execuție separat, se reduce riscul
de erori ”Aplicația Nu Răspunde”(ANR) și firul de execuție principal al aplicației poate să
rămână dedicat la interacțiunile utilizatorului cu activitățile.
Pentru a crea un serviciu, trebuie creată o subclasă a clasei ”Service” (sau una din
subclasele existente). În această implementare, trebuie suprascrise unele metode care
gestioneaz ă aspecte cheie ale ciclului de viață ale aplicației și oferă un mecanism pentru
componente pentru a se lega de servicii, dacă este cazul. Cele mai importante metode
sunt:
 onStartCommand() – Sistemul apelează această metodă când o altă componentă,
ca de ex emplu o activitate, face o cerere ca serviciul să fie pornit, prin apelarea
metodei ”startService()”. Odată ce această metodă se execută, serviciul este
pornit și poate rula în fundal pe termen nelimitat. Dacă se implementează această
metodă, serviciul tre buie oprit când și -a terminat activitatea, prin apelarea
metodelor ”stopSelf()” sau ”stopService()”. (Dacă se dorește doar oferirea de
legături, nu este necesară implementarea acestei metode.)
 onBind() – Sistemul apelează această metodă când o altă compone ntă dorește să
facă legătură cu serviciul, prin apelarea metodei ”bindService()”. În cadrul
implementării acestei metode, trebuie oferită o interfață pe care clienții să o
folosească pentru a comunica cu serviciul, prin întoarcerea unui obiect ”IBinder”.
Această metodă trebuie întotdeauna implementată, dar dacă nu se dorește
permiterea de legături, această metodă ar trebui să întoarcă null.
 onCreate() – Sistemul apelează această metodă când serviciul este creat pentru
prima dată, pentru a realiza proceduri de configurare o singură dată (înainte de a
apela ”onStartCommand()” sau ”onBind()”). Dacă serviciul rulează deja, această
metodă nu este apelată.
 onDestroy() – Sistemul apelează această metodă când serviciul nu mai este folosit
și este în curs de distruge re. Serviciul ar trebui să implementeze această metodă

27
pentru a elibera resurse, ca de exemplu fire de execuție, receptoare, etc. Aceasta
este ultima apelare pe care o primește serviciul.
Dacă o componentă pornește serviciul prin apelarea metodei ”startSe rvice()”,
atunci serviciul rămâne în execuție până în momentul în care se oprește singur apelând
metoda ”stopSelf()” sau o altă componentă îl oprește prin apelarea metodei
”stopService()”.
Dacă o componentă apelează metoda ”bindService()” pentru a crea un serviciu (și
metoda ”onStartCommand()” nu se apelează), atunci serviciul se execută atât timp cât
este legat de componentă. Odată ce serviciul este dezlegat de toți clienții, sistemul îl
distruge.
Sistemul de operare va opri forțat un serviciu când memor ia este scăzută și trebuie
să recupereze resurse ale sistemului pentru activitatea care are focusul. Dacă un serviciu
este legat de o activitate care are focusul utilizatorului, atunci este mai puțin probabil să
fie distrus, și dacă serviciul este declarat să ruleze în prim -plan, atunci nu va fi distrus
aproape niciodată. Altfel, dacă serviciul a fost pornit și este de lungă durată, atunci
sistemul îi va scădea poziția în lista de task -uri de fundal și, în timp, serviciul va fi foarte
susceptibil pentru a f i distrus – dacă un serviciu este pornit, acesta trebuie proiectat să
gestioneze reporniri de către sistem. Dacă sistemul distruge serviciul , acesta repornește
atunci când resursele devin disponibile din nou.
Pentru a declara un serviciu, este necesară ad ăugarea elementului ” <service> ”,
ca copil al elementului ” <application> ”. De exemplu:

<manifest … >

<application … >
<service android:name =".ServiciuExemplu " />

</application>
</manifest>
Există și alte atribute care pot fi incluse într -un element ”<service> ” pentru a defini
proprietăți ca permisiunile necesare pentru a porni serviciul și procesele în cadrul cărora
serviciul ar trebui să ruleze. Atributul ”android:name” este singurul atribu t necesar –
acesta specifică numele clasei serviciului.

28
Pentru securitatea aplicației, este necesară folosirea unei intenții explicite când se
pornește sau se leagă serviciul, și nu este recomandată filtre de intenție pentru serviciu.
Dacă este important să se ofere puțină ambiguitate pentru ca serviciul să pornească, se
pot oferi filtre de intenție serviciului și se poate exclude numele componentei din ”Intent”,
dar apoi trebuie setate pachetele pentru intenție folosind metoda ”setPackage()”, care
oferă s uficientă dezambiguizare pentru serviciul țintă.
Crearea unui serviciu pornit
Un serviciu pornit este acela care este pornit de o altă componentă prin apelarea
metodei ”startService()”, rezultând într -o apelare a metodei ”onStartCommand()” .
Când un servi ciu este pornit, acesta are un ciclu de viață care este independent
de componenta care l -a pornit și acesta poate rula în fundal pe perioadă nedeterminată,
chiar dacă componenta care l -a pornit este distrusă. Ca atare, serviciul ar trebui să se
oprească câ nd obiectivul este gata prin apelarea metodei ”stopSelf()”, sau o altă
componentă îl poate închide prin apelarea metodei ”stopService()”.
O componentă a unei aplicații ca de exemplu o activitate, poate porni serviciul prin
apelarea metodei ”startService() ” și trimiterea ca parametru a unui obiect ”Intent” care
specifică serviciul și include orice date pentru ca serviciul să le folosească.
De exemplu, dacă o activitate trebuie să salveze date într -o bază de date online,
activitatea poate să pornească un se rviciu și să îi trimită acestuia datele prin trimiterea
unui ”Intent” către metoda ”onStartService()”. Serviciul primește obiectul în cadrul
metodei ”onStartCommand()”, se conectează la Internet și realizează tranzacția bazei de
date. Când această tranzacț ie este finalizată, serviciul se oprește și este distrus.
În mod obișnuit, crearea unui serviciu se face prin extinderea următoarelor două
clase:
 Service – Aceasta este clasa de bază pentru toate serviciile. Când se extinde
această clasă, este important s ă se creeze un nou fir de execuție în care serviciul
să își desfășoare activitatea, deoarece serviciul folosește implicit firul de execuție
principal al aplicației , ceea ce poate să reducă performanțele oricărei activități
executată în aplicație.
 IntentSer vice – Aceasta este o subclasă a clasei Service care folosește un fir de
execuție lucrător pentru a gestiona toate cererile de pornire, una câte una. Aceasta
este cea mai bună soluție dacă serviciul nu trebuie să gestioneze cereri multiple

29
simultan. Tot ce ea ce este nevoie este implementarea metodei ”onHandleEvent()”,
care primește intenția pentru fiecare cerere de pornire.

Extinderea clasei IntentService

Deoarece cele mai multe servicii pornite nu au nevoie să gestioneze cereri multiple
simultan, este cel mai bine ca serviciul să fie implementat folosind clasa ”IntentService”.
Aceasta realizează următoarele:
 Creează un fir de execuție implicit care execută toate intențiile trimise la metoda
”onStartCommand()” separat de firul de execuție principal al ap licației.
 Creează o coadă de lucru care trimite intenții una câte una la implementarea
metodei ”onHandleIntent()”.
 Oprește serviciul după ce toate cererile de pornire au fost gestionate, pentru a nu
mai fi necesară apelarea metodei ”stopSelf()”.
 Oferă o im plementare implicită a metodei ”onBind()” care întoarce null.
 Oferă o implementare implicită a metodei ”onStartCommand()” care trimite intenția
cozii de lucru și mai apoi implementării metodei ”onHandleIntent()”.
Mai jos este un exemplu de implementare a c lasei ”IntentService”:
public class ExempluIntentService extends IntentService {

/**
* Un constructor este necesar, și trebuie apelat constructorul superclasei
IntentService(String) , ce primește ca parametru numele firului de execuție
*
*/
public ExempluIntentService () {
super("ExempluIntentService" );
}

/**
* IntentService apelează această metodă din firul de execuție implicit cu
* intenția care a pornit serviciul.Când această metodă întoarce un rezultat,
IntentService oprește serviciul.
*/
@Override
protected void onHandleIntent (Intent intent) {
// Firul de execuție așteaptă 5 secunde .
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Se restabilește starea de întarupere.

30
Thread.currentThread ().interrupt ();
}
}
}
Dacă se dorește suprascrierea altor metode, cum ar fi ”onCreate()”,
”onStartCommand()” , etc., este nevoie de apelarea implementării din superclasă, pentru
ca ”IntentService” să gestioneze corespunzător viața firului de execuție.
De exemplu, metoda ”onStartCommand()” trebuie să întoarc ă implementarea
implicită:
@Override
public int onStartCommand (Intent intent, int flags, int idStart) {
Toast.makeText (this, "serviciul porneste" , Toast.LENGTH_SHORT ).show();
return super.onStartCommand (intent,flags,idStart);
}
În afară de metoda ” onHandleIntent()”, singura metodă din care nu este necesară
apelarea implementării din superclasă este ”onBlind()” (însă implementarea acesteia este
necesară numai dacă serviciul permite legarea).

Extinderea clasei Service

Folosirea clasei IntentService face implementarea pornirii unui serviciu foarte
simplă. Dacă însă este necesar ca serviciul să permită multi -threading (în loc să
proceseze cererile de start printr -o coadă de lucru), atunci se poate extinde clasa Service
pentru a gestiona f iecare intenție.
Pentru comparație, următorul exemplu este o implementare a clasei Service care
realizează aceleași lucruri ca și exemplul anterior.
public class ExempluService extends Service {
private Looper mServiceLooper ;
private ServiceHandler mServiceHandler ;

// Handler care primește mesaje de la firul de exexuție
private final class ServiceHandler extends Handler {
public ServiceHandler (Looper looper) {
super(looper);
}
@Override

31
public void handleMessage (Message msg) {
// Firul de execuție așteaptă pentru 5 secunde .
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// Se restabilește starea de întarupere .
Thread.currentThread ().interrupt ();
}
// Se oprește serviciul folosind un startId, pentru a nu se opri
// în timpul gestionării altor lucruri
stopSelf (msg.arg1);
}
}

@Override
public void onCreate () {
// Pornirea firului de execuție ce execută serviciul.Se crează
// un fir de execuție separat, pentru că serviciul normal se execută în firul de
//execuție principal , care nu se vrea a fi blocat. De asemenea,
// i se oferă prioritate de fund al în așa fel încât o forțare a procesorului să
//nu afecteze interfața cu utilizatorul .
HandlerThread thread = new HandlerThread ("ServiceStartArguments" ,
Process.THREAD_PRIORITY_BACKGROUND );
thread.start();

mServiceLooper = thread.getLooper ();
mServiceHandler = new ServiceHandler (mServiceLooper );
}

@Override
public int onStartCommand (Intent intent, int flags, int startId) {
Toast.makeText (this, "service starting" , Toast.LENGTH_SHORT ).show();

// Pentru fiecare cerere de pornire , se trimite un mesaj pentru a trimite
//start ID -ul pentru a se cunoaște care cerere trebuie oprită.
Message msg = mServiceHandler .obtainMessage ();
msg.arg1 = startId;
mServiceHandle r.sendMessage (msg);

// Dacă serviciul este distrus, după ce întoarce un rezultat se repornește
return START_STICKY ;
}

@Override
public IBinder onBind(Intent intent) {
// Nu se oferă suport pentru legare, deci se întoarce null
return null;
}

@Override
public void onDestroy () {
Toast.makeText (this, "service done" , Toast.LENGTH_SHORT ).show();

32
}
}
Deoarece se gestionează fiecare apelare de către ”onStartCommand()”, se pot
realiza cereri multiple simultan. Exemplu de mai sus nu face acest lucru, dar se poate
crea un fir de execuție nou pentru fiecare cerere și se pot pune în execuție imediat (în loc
să se aștepte ca cererea anterioară să termine).
Se observă că metoda ” onStartCommand()” trebuie să întoarcă un întreg. Acest
întreg este o valoare ce descrie cum sistemul ar trebui să continue serviciul în
eventualitatea în care sistemul îl distruge. Valoarea întoarsă de ”onStartCommand()”
trebuie să fie una din următoarele constante:
 START_NOT_STICKY – Dacă sistemul distruge serviciul după
”onStartCommand()”, nu se recreează serviciul, decât dacă există intenții ce se
așteaptă a fi trimise. Aceasta este cea mai sigură metodă pentru a se evita
executarea serviciului când nu e ste necesar și când aplicația poate să
repornească orice muncă neterminată.
 START_STICKY – Dacă sistemul distruge serviciul după ”onStartCommand()”,
recreează serviciul și apelează ”onStartC ommand()”, dar nu trimite ultima intenție.
În schimb, sistemul ape lează ”onStartCommand()” cu o intenție nulă, în afara
situației în care sunt intenții în așteptare pentru a porni serviciul, caz în care
acestea sunt trimise. Aceasta este potrivită pentru playerele media care nu
execută comenzi, dar rulează pentru un timp nedeterminat și așteaptă de lucru.
 START_DELIVERY_INTENT – Dacă sistemul distruge serviciul după
”onStartCommand()”, recreează serviciul și apelează această metodă cu ultima
intenție care a fost trimisă serviciului. Orice intenție aflată în așteptare este
trimisă în ordine. Aceasta este potrivită pentru servicii care realizează o muncă
ce trebuie imediat reluată, ca descărcarea unui fișier.

Pornirea unui serviciu

Un serviciu se poate porni dintr -o activitate sau o altă componentă a aplicației prin
trimit erea unui obiect ”Intent”. Sistemul apelează metoda ”onStartCommand()” a
serviciului și îi trimite obiectul . De exemplu, o activitate poate porni un serviciu folosind
obiectul cu metoda ”startService()”:

33
Intent intent = new Intent(this, ExempluService .class);
startService (intent);
Metoda ”startService()” întoarce un rezultat imediat și sistemul apelează metoda
”onStartCommand()” a serviciului. Dacă serviciul nu este deja în execuție, sistemul
apelează prima dată metoda ”onCreate()”, apoi apelează meto da ”onStartCommand()”.
Dacă serviciul nu oferă legare, intenția trimisă cu metoda ”startService()” este
singura metodă de comunicare între componenta aplicației și serviciu. Însă, dacă se
dorește ca serviciul să trimită un rezultat înapoi, atunci clientul pornește serviciul și crează
un obiect de tip ”PendingIntent” pentru un broadcast (folosind ”getBroadcast()”) și
trimițându -l la serviciu în obiectul ”Intent” ce pornește serviciul.
Cereri multiple pentru pornirea serviciului rezultă în multiple apelăr i ale metodei
”onStartCommand()” a serviciului. Însă numai o cerere pentru oprirea serviciului este
necesară pentru a -l opri.

Oprirea unui serviciu

Un serviciu pornit trebuie să -și gestioneze ciclul de viață. Aceasta înseamnă că
sistemul nu oprește sau distruge serviciul decât dacă acesta trebuie să recupereze
memorie și serviciul continuă să ruleze după ”onStartCommand()” . Deci, serviciul trebuie
să se oprească singur prin apelarea metodei ”stopSelf()” sau o altă componentă îl poate
opri prin apelarea m etodei ”stopService()”. Odată solicitat să se oprească folosind una
din aceste metode, sistemul distruge serviciul cât de curând.
Cu toate acestea, dacă serviciul gestionează cereri multiple către
”onStartCommand()” în mod concurent, atunci serviciul nu a r trebui oprit când s -a
procesat o cerere de start, deoarece o nouă cerere poate apărea. Pentru a evita aveastă
problemă, se poate folosi metoda ”stopSelf(int)” pentru ca cererea de a opri serviciul să
fie întotdeauna bazată pe cea mai recentă cerere de po rnire. Aceasta înseamnă că,
atunci când se apelează metoda ”stopSelf(int)”, se trimite ID -ul cererii de start (startID -ul
trimis la ”onStartCommand()”) către care cererea de oprire răspunde. Dacă apoi serviciul
primește o nouă cerere de start înainte de a se apela metoda ”stopSelf(int)”, atunci ID -ul
nu va corespunde și serviciul nu se va opri.

Crearea unui serviciu legat

34

Un serviciu legat este acela care permite componentelor aplicației să se lege de
acesta prin apelarea metodei ”bindService()” pentru a crea o conexiune pe termen lung
(în general nu permite componentelor să îl pornească prin apelarea metodei
”startService()”).
Crearea unui serviciu legat este necesară atunci când se dorește să se
interacționeze cu serviciul din activități sau alte compo nente din aplicație pentru a expune
unele funcționalități la alte aplicații, prin comunicarea între procese (IPC).Pentru crearea
unui serviciu legat, este necesară implementarea metodei ”onBind()” pentru a întoarce
un obiect ”IBinder” ce definește interfaț a pentru comunicarea cu serviciul. Alte
componente ale aplicației pot apela apoi metoda ”bindService()” pentru a primi interfața
și începe a apela metode pe serviciu. Serviciul este activ doar pentru a servi componenta
aplicației de care este legat, deci c ând nu mai sunt componente legate de serviciu,
sistemul îl distruge.
Pentru a crea un serviciu legat, primul lucru de care este nevoie este definirea
interfeței ce specifică cum clientul poate comunica cu serviciul. Această interfată între
serviciu și cli ent trebuie să fie o implementare a ”IBinder”, aceasta fiind și tipul returnat în
metoda ”onBind()”. Odată ce clientul primește un ”IBinder”, poate începe interacțiunea cu
serviciu prin acea interfață.
Mai mulți clienți se pot lega de serviciu în același timp. Atunci când un client nu
mai interacționează cu serviciul, acesta apelează metoda ”unbindService()” pentru
dezlegare. Odată ce nu mai sunt clienți legați la serviciu, sistemul îl distruge .

Trimiterea notificărilor la utilizator

O notificare ”toast” este un mesaj care apare pe suprafața ferestrei curente pentru
un moment și apoi dispare, în timp ce o notificare pe bara de stare oferă o icoană în bară
cu n mesaj, pe care utilizatorul îl poate selecta pentru a realiza o acțiune.
De obicei, o notificare în bara de stare este cea mai bună tehnică atunci când s -a
terminat muncă în fundal (ca de exemplu atunci când un fișier s -a descărcat complet) și
utilizatorul poate acționa asupra lui. Când utilizatorul selectează notificare din vedere,
aceasta poate por ni o activitate (ca de exemplu vizualizarea fișierului descărcat).

Executarea unui serviciu în prim -plan

35

Un serviciu aflat în prim -plan este un serviciu care este considerat a fi ceva de care
utilizatorul este conștient, deci nu un candidat pentru sistem pentru a -l distruge în cazul
în care nu este suficientă memorie. Un asemenea serviciu trebuie să ofere o notificare
pentru bara de stare, plasată sub rubrica ”Ongoing”, ceea ce însemană că notificare nu
poate fi respinsă, decât dacă serviciul este fie opr it, fie eliminat din prim -plan.
De exemplu, un player de muzică ce redă muzică dintr -un serviciu ar trebui setat
să ruleze în prim -plan, pentru că utilizatorul este conștient de acesta. Notificare din bara
de stare ar putea indica piesa curentă și permite utilizatorului să lanseze o activitate
pentru a interacționa cu playerul.
Pentru a cere ca serviciul să ruleze în prim -plan, se apelează metoda
”StartForegroud()”. Aceasta preia doi parametri: un întreg ce identifică în mod unic
notificarea și un obiect ” Notification” pe ntru bara de stare. De exemplu:

Notification notificare = new Notification (R.drawable .icon,
getText(R.string.ticker_text ),
System.currentTimeMillis ());
Intent intentNotif = new Intent(this, ExampleActivity .class);
PendingIntent pendingIntent = PendingIntent .getActivity (this, 0, intentNotif , 0);
notificare .setLatestEventInfo (this, getText(R.string.titlu_notificare ),
getText(R.string.mesaj_notificare ), pendingIntent );
startForeground (ONGOING_NOTIFICATION_ID , notificare );

Pentru a elimina un serviciu din prim -plan, se apelează metoda
”stopForeground()”. Aceast metodă primește un boolean, ce indică dacă notificarea
pentru bara de stare va fi eliminată, de asemenea. Această metodă nu oprește serviciul.
Însă dacă se oprește se rviciul în timp ce acesta se află în execuție în prim -plan, atunci
notificarea va fi, de asemenea eliminată.

Gestionarea ciclului de viață al unui serviciu

Ca și în cazul activităților, un serviciu are metode ale ciclului de viață ce pot fi
implementate pentru a monitoriza schimbări în ceea ce privește starea serviciului și
realiza muncă la momentul potrivit. Următorul schelet de serviciu arată fiecare metodă
din ciclul de viață:

36
public class ExampluService extends Service {
int mModStart ; // idică comportamentul în cazul când srviciul este distrus
IBinder mBinder; // interfață pentru clienții care se leagă
boolean mPermiteRelegare ; // metoda onRebind ar trebui utilizată sau nu

@Override
public void onCreate () {
// Serviciul este creat
}
@Override
public int onStartCommand (Intent intent, int flags, int startId) {
// Serviciul este pornit datorită apelării metodei startService()
return mStartMode ;
}
@Override
public IBinder onBind(Intent intent) {
// Un clent este legat la serviciu cu metoda bindService()
return mBinder;
}
@Override
public boolean onUnbind (Intent intent) {
// Toți clienții s -au dezlegat folosind metoda unbindService()
return mAllowRebind ;
}
@Override
public void onRebind (Intent intent) {
// Un client se leagă la serviciu folosind bindService() ,
// după ce metoda onUnbind() a fost deja apelată
}
@Override
public void onDestroy () {
// Serviciul nu mai este folosit și este distrus
}
}
Spre deosebire de ciclul de viață al unei activități, nu este necesară apelarea
implementării din superclasă a acestor metode.

37

Fig.4 – Ciclul de viață al unui serviciu

Prin implementarea acestor metode se pot observa două bucle imbricate ale
ciclului de viață ale serviciului:
 Întregul ciclu de viață al serviciulu i are loc între timpul când este apelată metoda
”onCreate()” până când metoda ”onDestroy()” returnează. Ca și în cazul unor
activități, serviciul își face configurația inițială în cadrul metodei ”onCreate()” și
eliberează toate resursele rămase în ”onDestr oy()”. De exemplu, un serviciu de
redare a muzicii poate crea un fir de execuție unde muzica va fi redată în metoda
”onCreate()”, apoi să oprească firul de execuție în metoda ”onDestroy()”. Metodele
”onCreate()” și ”onDestroy()” sunt apelate pentru toate s erviciile, fie că sunt create
de ”startService()” sau ”bindService()”.
 Ciclul de viață activ al serviciului începe cu apelarea fie a metodei
”onStartCommand()”, fie a metodei ”onBind()”. Fiecărei metode îi este trimis
obiectul ”Intent” care a fost trimis f ie către ”startService()” sau ”bindService()”.

38
Dacă serviciul este pornit, ciclul de viață activ se termină în același timp cu ciclul
de viață întreg. Dacă serviciul este legat, ciclul de viață activ se termină când
metoda ”onUnbind()” se sfârșește.

2.2.3. Furnizorii de conținut (Content providers)

Furnizorii de conținut gestionează accesul la un structurat de date. Aceștia
încapsulează datele și oferă mecanisme pentru a defini securitatea datelor. Furnizorii de
conținut sunt interfața standard care c onectează data într -un proces și codul care rulează
în alt proces.
Atunci când se dorește accesarea datelor într -un furnizor de conținut, se folosește
un obiect ”ContentResolver” în contextul aplicației cu furnizorul ca și client. Obiectul
”ContentProvider ” comunică cu obiectul furnizor, o instanță a unei clase ce
implementează ”ContentProvider”. Obiectul furnizor primește cereri pentru date din
partea clienților, realizează acțiunea cerută, și întoarce rezultatele.
Android include furnizori de conținut car e gestionează date precum audio, video,
imagini, și informații de contact personale. Un furnizor face parte dintr -o aplicație Android,
care de obicei oferă o interfață cu utilizatorul pentru a lucra cu date. Cu toate acestea,
furnizorii de conținut sunt de stinați să fie folosiți de alte aplicații, care accesează furnizorul
folosind un obiect furnizor client. Împreună, furnizorii și furnizorii clienți oferă o interfață
standard pentru date care gestionează de asemenea comunicarea între procese și
accesul sec urizat la date.
Accesarea unui furnizor

O aplicație accesează date dintr -un furnizor de conținut cu un obiect
”ContentResolver” client. Acest obiect are metode care apelează metodele cu același
nume în obiectul furnizor, o instanță a uneia dintre subclase le clasei ”ContentProvider”.
Metodele din cadrul acestei clase oferă funcțiile de bază CRUD (create, retrieve, update,
delete) de stocare persistentă.
Obiectul ”ContentResolver” din procesul aplicației client și obiectul
”ContentProvider” din aplicația ce deține furnizorul gestionează automat comunicarea
între procese. De asemenea ”ContentProvider” acționează ca un strat de abstractizare
între depozitul de date și apariția externă a datelor ca tabele.

39
De exemplu, pentru a obține o listă de cuvinte și locați ile acestora din ”User
Dictionary Provider”, se apelează ”ContentResolver.query()”. Metoda ”query()” apelează
metoda ”ContentProvider.query()”, definită de furnizor. Exemplul de mai jos arată un apel
către metoda ”ContentResolver.query()”:
// Interoghează dicționarul și întoarce rezultatele
mCursor = getContentResolver ().query(
UserDictionary .Words.CONTENT_URI , // URI-ul conținutului tabelului de cuvinte
mProjection , // Coloanele întoarse pentru fiecare rând
mSelectionCl ause // Criteriu de selecție
mSelectionArgs , // Criteriu de selecție
mSortOrder ); // Ordinea de sortare

Pentru a prelua date de la furnizor, trebuie urmați următorii pași:
1. Cererea unei permisiuni de acces pentru citire pentru furnizor
2. Definirea codului care trimite o interogare către furnizor

Aplicația unui furnizor poate specifica permisiuni pe care alte aplicații trebuie să le
aibă pentru a accesa datele furnizorului. Aceste permisiuni asigură faptul că utilizatorul
știe ce dată va încerca să acceseze aplicația. Bazate pe cerințele furnizorului, alte aplicații
cer permisiunile de care au nevoie pentru a accesa furnizorul. Utilizatorul final vede
permisiunile cerute când instal ează aplicația.
Dacă aplicația unui furnizor nu specifică nicio permisiune, atunci alte aplicații nu
au acces la datele furnizorului. Cu toate acestea, componentele aplicației din care face
parte furnizorul au acces la citire și scriere, indiferent de perm isiunile specificate.
Pentru a obține permisiunile necesare pentru a accesa un furnizor, o aplicație le
cere folosind un element ”<uses -permission >” în fișierul manifest:
<uses-permission android:name ="android.permission.READ_USER_DICTIONARY" >
Inserarea, actualizarea și ștergerea datelor

Pentru a insera date într -un furnizor, este necesară apelarea metodei
”ContentResolver.insert()”. Această metodă inserează un nou rând în furnizor și întoarce

40
un URI al conținutului pentru acel rând. Exemplul de mai jos a rată inserarea unui cuvânt
în furnizorul ” User Dictionary Provider ”:
/ Se definește un nou obiect URI care primește rezultatul inserării
Uri mUriNou;

// Se definește un obiect ce conține noile valori de inserat
ContentValues mValoriNoi = new ContentValues ();

/*
* Setează valoarea fiecărei coloaneși inserează cuvântul . Argumentele metodei "put"
* suntnumele coloanei și valoarea */
mValoriNoi .put(UserDictionary .Words.APP_ID, "utilizator_nou ");
mValoriNoi .put(UserDictionary .Words.LOCALE, "en_US");
mValoriNoi .put(UserDictionary .Words.WORD, "insert" );
mValoriNoi .put(UserDictionary .Words.FREQUENCY , "100");

mUriNou = getContentResolver ().insert(
UserDictionary .Word.CONTENT_URI , // URI-ul conținutului user dictionary
mValoriNoi // valorile de inserat
);
Datele pentru noul rând intră într -un singur obiect ”ContentValues”, care este
similar cu un cursor pe un singur rând. În acest obiect coloanele nu trebuie să aibă același
tip de dată, și dacă nu se spec ifică o valoare, o coloană se poate seta ca nulă folosind
metoda ”ContentValues.putNull()”.

Actualizarea datelor

Pentru a actualiza un rând, se utilizează un obiect ”ContentValues” cu valorile
actualizate ca și când s -ar face o inserare, și criteriul de selecție la fel ca și în cazul
interogărilor. Metoda client folosită este ”ContentResolver.update()”.
Exemplul de mai jos actualizează toate rândurile a căror locație are limba ”en”,
setându -le acestora valoare ”null”. Valoarea întoarsă este numărul rând urilor ce au fost
actualizate:
// Se definește un obiect ce conține valorile actualizate
ContentValues mValoriActualizate = new ContentValues ();

// Se definește criterul de selecție pentru rândurile ce vor fi actualizate

41
String mClauzaSelectie = UserDictionary .Words.LOCALE + "LIKE ?" ;
String[] mArgSelectie = {"en_%"};

// Se definește o varialbilă ce conține numărul de rânduri șterse
int mRanduriActualizate = 0;

/*
* Se setează valoarea actualizată și se actualizează rândurile selectate .
*/
mValoriActualizate .putNull(UserDictionary .Words.LOCALE);

mRanduriActualizate = getContentResolver ().update(
UserDictionary .Words.CONTENT_URI , // URI-ul conținutului user dictionary
mValoriActualizate // coloana ce se actualizează
mClauzaSelectie // coloana din care se selectează
mArgSelectie // valoarea ce se compară
);

Ștergerea datelor

Ștergerea unui rând este similară cu citirea datelor de pe un rând; se specifică
criteriul de selecție pentru rânduri le ce urmează a fi șterse și metoda client întoarce
numărul de rânduri șterse. În exemplul de mai jos se șterg rândurile ale căror ”appid” este
egal cu ”user”:
// Se definește criteriul de selecție pentru rândurile ce se doresc a fi șterse
String mClauzaSelectie = UserDictionary .Words.APP_ID + " LIKE ?" ;
String[] mArgSelectie = {"user"};

// Se definește o variabilă ce conține numărul de rânduri șterse
int mRanduriSterse = 0;

// Se șterge rândul ce îndeplinește criteriul de selecție
mRanduriSterse = getContentResolver ().delete(
UserDictionary .Words.CONTENT_URI , // URI-ul conținutului user dictionary
mClauzaSelectie // coloana din care se selectează
mArgSelectie // valoarea ce se c ompară
);

42
Tipuri de date ale furnizorilor

Furnizorii de conținut pot oferi multe tipuri de date diferite. Datele pot fi oferite în
următoarele formate:
 Întreg
 Întreg lung (long)
 Virgulă mobilă
 Virgulă mobilă dublă precizie (double)
Un alt tip de dată care este folosit de furnizori este Binary Large OBject (BLOB)
implementat ca un vector de 64 de octeți. Tipul de dată pentru fiecare coloană dintr -un
furnizor este de obicei listat în propria documentație. Furnizorii mențin, de asemenea
informații MIME pentru tipurile de date pentru fiecare URI de conținut ce este definit.
Aceste tipuri de informații se folosesc pentru a afla dacă aplicația poate să gestioneze
datele pe care le oferă furnizorul sau pentru a alege cum se gestionează acest ea pe baza
unui tip MIME. Acest tip este utilizat de obicei când se folosește un furnizor ce conține
structuri de date complexe sau fișiere .

Crearea unui furnizor de conținut

Un furnizor de conținut gestionează accesul la un depozit central de date. Un
furnizor este implementat într -o aplicație Android ca una sau mai multe clase, împreună
cu elementele din fișierul manifest. Una dintre aceste clase implementează subclasa
”ContentProvider”, care este interfața dintre furnizor și alte aplicații. Deși fu rnizorii de
conținut sunt destinați să facă datele vizibile către alte aplicații, este posibil ca într -o
aplicație să existe activități care permit utilizatorilor să interogheze și să modifice datele
gestionate de furnizor.
Înainte de a se crea interfața între furnizor și aplicații trebuie decis cum se vor
stoca datele. Datele pot fi stocate în orice formă se dorește, și mai târziu se poate proiecta
interfața pentru a citi și scrie date când este necesar.

43
Câteva dintre tehnologiile de stocare de date în A ndroid sunt:
 Android include un API pentru baza de date SQLite pe care furnizorii proprii
Android o folosesc pentru a stoca date orientate -tabel. Clasa ”SQLiteOpenHelper”
permite crearea bazelor de date, și clasa ”SQLiteDatabase” este clasa de bază
pentru a accesa baze de date. Pentru a implementa un depozit (repository) nu
este necesară folosirea unei baze de date. Un furnizor apare extern ca un set de
tabele, similar cu o bază de date relațională, dar aceasta nu este o cerință pentru
implementarea internă a furnizorului de conținut.
 Pentru a stoca date din fișiere, Android are o varietatea de API -uri orientate spre
fișiere. Dacă se dorește proiectarea unui furnizor care oferă date media precum
muzică sau video, se poate lua în calcul un furnizor care combi nă date din tabel și
fișiere
 Pentru lucrul cu date bazate pe rețea, se folosesc clase din ”java.net” și
”android.net”. Datele din rețea se pot de asemenea sincroniza pe o bază de date
locală, și oferi apoi datele sub formă de tabele către utilizatori.
Mai jos sunt listate câteva sfaturi pentru proiectarea structurii de date a unui furnizor:
 Datele din tabele ar trebui să aibă întotdeauna o coloană ”cheie primară”, pe care
furnizorul o menține ca fiind o valoare numerică unică pentru fiecare rând.
Aceast ă valoare poate fi folosită pentru a lega rândul la alte rânduri asociate cu
acesta din alte tabele (folosindu -l ca cheie străină). Deși se poate folosi orice
nume pentru această coloană, folosirea unui nume ca ”Coloana._ID” este cea mai
bună soluție, deoa rece legarea rezultatelor unei interogări la un ”ListView”
necesită ca una dintre coloane să aibă numele ”_ID”.
 Dacă se dorește furnizarea de imagini bitmap sau alte fragmente de date de mari
dimensiuni orientate -fișier, atunci cea mai bună soluție este st ocarea acestora
într-un fișier care să fie apoi furnizat indirect, decât prin stocarea lui direct într -un
tabel. Dacă se alege această soluție, este necesară înștiințarea utilizatorilor de
faptul că se impune folosirea unei metode ”ContentResolver”.
 Se rec omandă folosirea unui tip de dată BLOB pentru a stoca date ce variază ca
mărime sau structură. De exemplu se poate folosi o colană BLOB pentru a stoca
un ”protocol buffer” sau o structură JSON.
Aplicațiile pot accesa un furnizor de conținut indirect prin intermediul unui
”Intent”.Aplicația nu apelează niciuna dintre metodele claselor ”ContentResolver” sau

44
”ContentProvider”. În schimb, aceasta trimite o intenție care execută o activitate, care
este de obicei parte a unei aplicații proprii a furnizorului. A ctivitatea destinație are
responsabilitatea de a prelua și afișa datele în interfața utilizator. În funcție de acțiunea
din intenție, activitatea destinație este posibil să determine utilizatorul să facă modificări
datelor furnizorului. O intenție poate de asemenea să conțină date suplimentarea pe care
activitatea destinație să le afișeze în interfața utilizator, utilizatorul având apoi opțiunea
să schimbe aceste date înainte de a le folosi pentru a modifica datele în furnizor.
Un acces pe bază de intenție poate fi folosit pentru a asigura integritatea datelor.
Furnizorul poate depinde sau avea date inserate, actualizate, și șterse în funcție de
logică. Dacă se întâmplă acest lucru, permiterea altor aplicații să modifice în mod direct
datel e poate duce la d ate invalide.

2.3.Fișierul Manifest
Înainte ca sistemul Android să pornească o componentă a aplicației, sistemul
trebuie să știe că acea componentă există prin citirea fișierului ”AndroidManifest.xml”
(fișierul ”manifest”). Toate componentele unei aplic ații se declară în acest fișier, care
trebuie să fie rădăcina directorului proiectului aplicației.
Acest fișier realizează numeroase lucruri pe lângă declararea componentelor
aplicației cum ar fi:
 Identifică orice permisiune cerută de aplicație, cum ar fi accesul la Internet sau
acces pentru citire la contactele utilizatorului
 Declară nivelul minim al API -ului care îl necesită aplicația, bazat pe ce API
folosește aplicația
 Declară caracteristici hardware și software folosite sau necesare aplicației, cum ar
fi camera, serviciile bluetooth, sau ecranul tactil.
 Librării API de care aplicația are nevoie să fie legată, cum ar fi Google Maps Library

Sarcina principală a fișierului manifest este de a informa sistemul despre
componentele aplicației. De exemplu, un fișier manifest poate declara o activitate în felul
următor:
<?xml version ="1.0" encoding ="utf-8"?>
<manifest … >
<application android:icon ="@drawable/app_icon.png" … >

45
<activity android:name ="com.exemplu.proiect.ActivitateExemplu "
android:label ="@string/ exemplu " … >
</activity>

</application>
</manifest>
În elementul ”< application> ”, atributul ”android:icon” face referire la resurse pentru
o icoană care identifică aplicația.În elementul ”<activity >”, atributul ”android:name”
specifică numele complet al subclasei clasei ”Activity” și elementul ”android:label”
specifică un string pentru a fi folosit ca etichetă pentru activitate.
Activitățile, serviciile și furnizorii de context care sunt incluși în sursă dar nu sunt
declarați în fișierul manifest nu sunt vizibili sistemului și, implicit nu vor putea fi executați.
Pentru a porni activități și servicii se pot folosi obiecte ”Intent”. Acest lucru se poate
realiza prin numirea expl icită a componentei țintă în intenție. Cu toate acestea, adevăratul
punct forte al intențiilor este conceptul de intenții implicite. O intenție implicită descrie
timpul de acțiune ce trebuie realizată (și, opțional, datele asupra cărora se dorește a fi
realizată acțiunea) și permite sistemului să găsească o componentă pe dispozitiv care
poate realiza acțiunea și să o pornească. Dacă există mai multe componente care
realizează acțiunea descrisă de intenție, atunci utilizatorul decide pe care să o
folosească.
Modalitatea prin care sistemul identifică componentele care pot răspunde la o
intenție este prin compararea intenției primite cu filtrele de intenție aflate în fișierul
manifest sau alte aplicații de pe dispozitiv.
Atunci când o activitate este declarat ă în fișierul manifest al aplicației, se pot
include filtre de intenție pentru componentă prin adăugarea unui element ”<intent -filter>”
ca un copil al elementului de declarare al componentei.
De exemplu, într -o aplicație de email ce conține o activitate pentru a compune un
nou email, se poate declara un filtru de intenție pentru a răspunde la intenții ”trimite”
(pentru a trimite un nou email), ca în exemplul de mai jos:
<manifest … >

<application … >
<activity android:name ="com.ememplu.proiect.ActivitateCompunereEmail ">
<intent-filter>
<action android:name ="android.intent.action.SEND" />
<data android:type ="*/*" />
<category android:name ="android.intent.category.DEFAULT" />

46
</intent -filter>
</activity>
</application>
</manifest>
Mai târziu, dacă o altă aplicație creează o intenție cu acțiunea ”ACTION_SEND” și
o trimite către ”startActivity()”, sistemul poate porni activitatea pentru ca utilizatorul să
poată compune un email nou.
Există o varietate de dispozitive ce rulează sistemul de operare Android, și niciunul
dintre acestea nu oferă aceleași caracteristici și capabilități. Pentru a împiedica o aplicație
de la a fi instalată pe dispo zitive care nu au caracteristicile cerute de aplicație, este
important să se definească un profil pentru tipurile de dispozitive care sunt suportate de
aplicație prin declararea cerințelor de dispozitiv și software în fișierul manifest. Multe din
aceste de clarații sunt doar informaționale și sistemul nu le citește, dar servicii externe ca
Google Play le citesc pentru a oferi filtrare pentru utilizatori atunci când aceștia caută
aplicații de pe dispozitiv.
De exemplu, dacă o aplicație necesită o cameră și u tilizează API -urile introduse în
Android 2.1 (nivel API 7), este necesară declararea acestor cerințe în fișierul manifest:
<manifest … >
<uses-feature android:name ="android.hardware.camera.any"
android:required ="true" />
<uses-sdk android:minSdkVersion ="7" android:targetSdkVersion ="19" />

</manifest>
Acum un dispozitiv ce nu este dotat cu cameră și are o versiune Android mai mică
de 2.1 nu poate instala aplicația de la Google Play. Cu toate acestea, se poate declara
că aplicația folosește camera dar nu are nevoie de aceasta. În acest caz, aplicația trebuie
să seteze atributul ”required” ca fiind ”false” și să verifice la execuție dacă dispozitivul are
cameră și să dezactiveze caracteristicile camerei.

2.4.Resursele aplicației
O aplicație Android nu este compusă doar din cod – aceasta necesită resurse care
sunt separate de codul sursă, cum ar fi imagini, fișiere audio, și alte componente ce au
legătură cu prezentarea vizuală a aplicației. De exemplu, animațiile, meni urile, stilurile,

47
culorile și layout -ul ar trebui definite într -un fișier XML. Folosirea resurselor face simplă
actualizarea diferitelor caracteristici ale aplicației fără a modifica codul și, prin oferirea
unor seturi de resurse alternative, permite optim izarea aplicației pentru o varietate de
configurări ale dispozitivelor (cum ar fi diferite limbi și mărimi de ecran).
Pentru fiecare resursă care este inclusă într -un proiect Android, uneltele SDK -ului
definesc un întreg unic, ID, care poate fi folosit p entru a face referire la resursă din codul
aplicației sau din alte resurse definite în XML. De exemplu, dacă aplicația conține un fișier
imagine numit ”imagine.png” (salvată în directorul ”res/drawable”), uneltele SDK -ului
generează o resursă ID numită ”R. drawable.imagine”, care poate fi folosită să facă
referire la imagine și poate fi inserată în interfața utilizator.
Unul dintre cele mai importante aspecte în a furniza resursele separat de codul
sursă este abilitatea de a furniza resurse alternative pent ru configurări diferite ale
dispozitivului. De exemplu, prin definirea textului folosit în interfața cu utilizatorul, se poate
traduce acel text în alte limbi, și se pot salva în fișiere separate. Mai târziu, bazat pe un
calificativ de limbă care este adău gat la numele directorului în care se află resursele (de
exemplu ”res/values -ro” pentru română) și pe setările de limbă ale utilizatorului, sistemul
Android aplică valorile adecvate pentru interfața utilizator.
Android suportă multe calificative diferite pentru resursele alternative. Calificativul
este un text scurt ce se include în numele directorului resurselor pentru a defini
configurarea dispozitivului pentru care acele resurse ar trebui să fie utilizate. De multe ori
este necesară crearea de diferite layout -uri pentru activități, ce depind de orientarea
ecranului dispozitivului și de dimensiunea acestuia De exemplu, când orientarea
ecranului dispozitivului este în modul portret, se poate crea un layout cu butoanele
așezate pe verticală, dar când ecranu l este orientat în modul vedere, butoanele pot fi
aliniate pe orizontală. Pentru a schimba layout -ul în funcție de orientare, se pot defini
două layout -uri diferite și se poate aplica calificativul adecvat pentru fiecare nume al
directorului. Apoi, sistemu l aplică în mod automat layout -ul adecvat în funcție de
orientarea curentă a dispozitivului.

Furnizarea de resurse alternative

Aproape fiecare aplicație ar trebui să furnizeze resurse alternative pentru a suporta
configurări specifice ale dispozitivelor . De exemplu, ar trebui incluse resurse ”drawable”
alternative pentru densități diferite ale ecranului și text alternativ pentru limbi diferite. La

48
execuție, Android detectează configurările curente ale dispozitivului ți încarcă resursele
adecvate pentru a plicație.

Fig.5 – Două dispozitive diferite, fiecare folosind resurse diferite ale layou t-ului

Pentru a specifica alternative specifice configurației pentru un set de resurse:
1. Se creează un director nou în ”/res” cu denumirea de forma ” <nume_resursă >-
<calificativ_config >”.
 <nume_resursă > este numele directorului resurselor corespunzătoare
implicite
 <calificativ_config > este un nume ce specifică o configurare individuală
pentru care aceste resurse vor fi utilizate.
2. Salvarea respectivei resurse alterna tive în acest nou director. Fișierele resursă
trebuie numite exact ca fișierele resursă implicite.
De exemplu, mai jos este un exemplu despre cum se pot defini resurse implicite și
alternative:
res/
drawable/
imagine.png
fundal.png
drawable -hdpi/
imagine.png
fundal.png

49
Calificativul ”hdpi” indică faptul că resursele din acel director sunt destinate
dispozitivelor cu un ecran de densitate mare. Imaginile din fiecare director sunt
dimensionate pentru o densi tate specifică a ecranului, dar numele fișierelor sunt exact la
fel. În acest fel, ID -ul resursei care se folosește pentru a face referire la ”imagine.png”
sau ”fundal.png” este întotdeauna același, însă Android selectează pentru fiecare resursă
versiunea care se potrivește cel mai bine dispozitivului curent, prin compararea
informațiilor de configurare ale dispozitivului cu calificativele din numele directorului
resurselor. Android suportă mai multe calificative de configurare și se pot adăuga multiple
calificative la numele unui director, prin separarea acestora cu o liniuță.

Reguli pentru denumirea calificativelor

Mai jos sunt lis tate câteva reguli pentru denumirea calificativelor de configurare:
 Se pot specifica nume multiple de calificative pentru un singur set de resurse,
separate printr -o liniuță. De exemplu ”drawable -en-rUS-land” se aplică la
dispozitivele ce au setată limba engleză și au orientarea ecranului în modul
vedere.
 Directoarele resurselor alternative nu pot fi imbricate. De exemplu, nu e ste posibilă
forma: ” res/drawable/drawable -en/”
 Doar o singură valoare este suportată pentru fiecare calificativ. De exemplu, dacă
se dorește folosirea acelorași fișiere de resurse pentru Spania și Franța, nu se
poate folosi un director numit ”drawable -rES-rFR/”. În schimb, sunt necesare două
directoare de resurse, ce conțin fișierele adecvate. Cu toate acestea, nu este
necesar a se duplica același fișier în ambele locații. În schimb, se poate crea un
”alias” pentru resursă.
După ce se salvează resursele alternative în directoarele denumite cu aceste
calificative, Android aplică automat resursele în aplicație, pe baza configurării curente a
dispozitivului. De fiecare dată când o resursă este solicitată, Android verifică dacă există
directoare pe ntru resurse alternative ce conțin resursa necesară, apoi găsește cea mai
potrivită resursă. Dacă nu există nicio resursă alternativă ce corespunde cerințelor, atunci
android folosește resursele implicite corespunzătoare (setul de resurse pentru un tip
particular de resursă care nu include un calificativ de configurare).
Pentru ca o aplicație să suporte mai multe configurări ale dispozitivului, este foarte
important să se furnizeze resurse implicite pentru fiecare tip de resursă ce este folosită

50
de aplicaț ie. De exemplu, dacă aplicația suportă mai multe limbi, se include un director
”values/” (în care sunt salvate textele), fără un calificativ pentru limbă și regiune. Dacă în
loc de aceasta se pun toate textele în directoare care au un calificativ pentru li mbă și
regiune, atunci aplicația se va închide atunci când va fi rulată pe un dispozitiv ce este
setat pe o limbă ce resursele aplicației nu o suportă. Dar, atât timp cât se furnizează
resurse implicite, atunci aplicația se va executa corespunzător (chiar dacă utilizatorul nu
va înțelege limba – este o variantă mia bună decât închiderea aplicației).
De asemenea, dacă se furnizează diferite resurse de layout bazate pe orientarea
ecranului, ar trebui aleasă o orientare ca fiind implicită. De exemplu, în loc să se furnizeze
resurse de layout în directorul ”layout -land/” pentru orientarea tip vedere și ”layout -port/”
pentru orientarea tip portret, se recomandă utilizarea unuia dintre aceste directoare ca
director implicit, de exemplu, folosirea ”layout/” pentru orientarea tip vedere și ”layout –
port/” pentru orientarea tip portret.
Furnizarea de resurse implicite este importantă nu numai pentru că aplicația este
posibil să ruleze pe o configurare ce nu a fost anticipată, dar și deoarece versiunile noi
de Android adaugă uneori calificative de configurare pe care vechile versiuni nu le
suportă. Dacă se folosește un calificativ nou pentru resurse, dar se păstrează
compatibilitatea codului cu vechile versiuni de Android, atunci când o versiune mai veche
de Android ex ecută aplicația, aceasta se va închide dacă nu se furnizează resurse
implicite, deoarece nu poate folosi resursele denumite cu noile calificative.
Deci, pentru a furniza cea mai bună compatibilitate cu dispozitivele, este
recomandată furnizarea de resurse implicite pentru resursele necesare aplicației pentru
ca aceasta să ruleze în mod corespunzător. De asemenea, se recomandă crearea de
resurse alternative pentru configurările specifice ale dispozitivelor, folosind calificativele
de configurare.

51
3.Note – aplicație Android pentru gestionarea de note
Aplicația Android realizată are ca scop gestionarea notelor unui utilizator. Un
utilizator poate adăuga, șterge, modifica notele și este posibilă vizualizarea acestora pe
categorii sau favorite. De asemenea, un utilizator are posibilitatea de a căuta o not ă după
titlul acesteia sau după conținut. O notă poate fi adăugată la favorite, ceea ce înseamnă
că se poate găsi în lista pentru favorite, ce poate fi accesată din meniu. Aplicația are un
aspect plăcut, în conformitate cu specificațiile Material Design pe ntru Android.
Atunci când aplicația este executată pentru prima dată, este afișat un dialog în
care utilizatorul își poate introduce numele. Acest nume poate fi modificat mai târziu din
meniu.

Fig.6 – Dialogul ce se afișează automat la prima executare a aplicației
După ce utilizatorul setează numele (opțional), se va afișa ecranul principal al
aplicației, în care se pot vizualiza toate notele. Dacă este prima utilizare, atunci nu se va
găsi nicio notă în acest ecran și se va afișa o imagine și un text .

52

Fig. 6 – În cazul în care nu există note adăugate, se va afișa ecranul din dreapta, iar dacă
există note se va afișa o listă cu toate notele utilizatorului.

Datele sunt stocate într -o bază de date SQLite , ce conține două tabele: ”Note”
pentru stocarea notelor, respectiv ”Category” pentru stocarea categoriilor. Clasele ”Note”
și ”Category” sunt entități ce fac parte din model, POJO -uri folosite pentru a realiza
operații în baza de date cu ajutorul acestora. Între cele două tabele există o relație ”1 -n”,
ceea ce înseamnă că o categorie poate avea mai multe note, însă o notă nu poate avea
mai multe categorii. În ceea ce privește entitățile, această relație se realizează cu ajutorul
agregării, un obiect de tip ”Note” având ca atribut un obiect de tip ”Category”. Pentru a
realiza operații CRUD (Create, Read, Update, Delete) asupra unei baze de date SQLite,
este necesară extinderea clasei ” SQLiteOpenHelper ”. De exemplu pentru a insera o notă
nouă în baza de date se folosește meto da ”insertNote”, ce primeșt e ca parametru un
obiect de tip ”Note”:

53

În cadrul acestei metode se creează un obiect ” ContentValues ”, care este folosit
pentru a stoca un set de date ce pot fi procesate de către un ”ContentResolver”. Metoda
”put” primește ca parametrii o cheie și o valoare și are rolul de adăuga aceste informații
în obiect. În acest caz, cheia este numele coloanei din tabelul bazei de date, iar valoarea
este atributul obiectului entitate. După ce s -au introdus datele, este apelată metoda
”insert” din cadrul clasei ”SQLiteDatabase”, ce primește ca parametrii numele tabelului în
care se inserează, un String (” nullColumnHack ”), ce nu permite inserarea unui rând gol
dacă este null, și obiectul de tip ”ContentValues” în car e s-au adăugat anterior date.
Pentru actualizarea și ștergerea de note se folosesc metode asemănătoare, în cadrul
cărora se apelează metodele specifice din clasa ”SQLiteDatabase” ( ”update”, respectiv
”delete” ).
Pentru întoarcerea tuturor notelor se folose ște o abordare diferită. Inițial este
creată o listă cu obiecte ”Note”, apoi se definește și se execută o interogare SQL, și cu
ajutorul unui cursor se parcurg datele. Pentru fiecare rezultat, acesta se adaugă în lista
creată inițial. La finalul metodei, s e întoarce lista populată cu rezultatele interogării:
public void insertNote(Note note) {
ContentValues values = new ContentValues();
values.put( COL_N_TITLE , note.getTittle());
values.put( COL_N_CONTENT , note.getContent());
values.put( COL_N_FAV , note.getFavourite());
values.put( COL_N_LAST_EDIT , note.getLastEdit());
values.put( COL_N_CATEGORY , note.getCategory().getId());
try {
db.insert( TABLE_NOTE , null, values);
} catch (Excepti on e) {
Log. e("DB ERROR" , e.toString());
e.printStackTrace();
}
}

public List<Note> getAllNotes() {
List<Note> notes = new ArrayList<>();
try {
Cursor cur = db.rawQuery( "SELECT * FROM " + TABLE_NOTE , null);
cur.moveToFirst();
if (!cur.isAfterLast()) {
do {
notes.add(getNoteFromCursor(cur));
} while (cur.moveToNext());
}
} catch (Exception e) {
e.printStackTrace();
Log. e("DB ERROR" , e.toString());
}
return notes;
}

54
În ceea ce privește interfața cu utilizatorul, operațiile de ștergere, modificare și
adăugare se realizează în același layout. În cadrul acestuia se găsesc câmpuri pentru
introducerea titlului, respectiv conținutului unei note, un buton pentru salvarea
modificărilor și opțional butoane pe bara de acțiune pentru ștergerea unei note sau
adăugarea la favorite (în cazul în care nota este deja adăugată și se dorește modificarea).

Fig. 7 – Layout -ul de adăugare notă (dreapta), respectiv layout -ul folosit pentru
modificarea, ștergerea sau adăugarea unei note la favorite. Față de layout -ul pentru
adăugarea unei note, cel pentru editare și ștergere conține butoane pe bara de activități
pentru adăugarea la favorite, respectiv ștergerea unei note. De asemenea se afișează
data la care a fost ultima dată modificată nota.

Butonul pentru salvare apelează aceeași metodă indiferent dacă se adaugă sau
se modifică nota. Această metodă se găsește în activitatea aferentă layout -ului, respectiv
”ActivityEditNote”. În cadrul acestei metode se face mai întâi o validare a câmpurilor
pentru a se asigura completarea câmpurilor, după care se verifică dacă nota există deja
în baza de date sau nu. Dacă se află deja în baza de date și se dorește a fi modificată,
după apăsarea butonului ”SALVEAZĂ” se va apela metoda ”actionSave()” din cadrul
activității, în care se vor seta noile valori, pe lângă care și data ultimei modificări și se va
apela metoda ”update(Note note)” din ca drul clasei ce se ocupă cu realizarea operațiilor

55
din baza de date, respectiv ”DatabaseManager” . Dacă nota nu există în baza de date,
atunci se va apela metoda ” insert(Note note) ” din cadrul aceleiași clase:

Pentru a crea liste complexe și carduri cu stiluri Material Design se folosesc widget –
uri ”CardView”, respectiv ”RecycleView”. Widget -ul ”RecycleView” este o versiune mai
avansată și mai flexibilă a ”ListView”. Acesta este un recipient pentru afișarea de seturi
de date mari ce pot fi parcurse foarte eficient prin menținerea unui număr limitat de
vizualizări. Acesta simplifică afișarea și manipularea datelor prin oferirea:
 Manageri de layout pentru poziționarea elementelor
 Animații implicite pen tru operații comune, cum ar fi adăugarea sau ștergerea
elementelor

Fig. 8 – Widget -ul RecyclerView
private void actionSave() {
hideKeyboard( );
if (tittle.getText().toString().equals( "") ||
content.getText().toString().equals( "")) {
Snackbar. make(parent_view , "Titlul sau conținutul nu pot fi goale" ,
Snackbar. LENGTH_SHORT ).show();
} else {
if(is_new) ext_note = new Note();
String notif_text;

ext_note .setTittle( tittle.getText().toString());
ext_note .setContent( content.getText().toString());
ext_note .setLastEdit(System. currentTimeMillis ());
ext_note .setCategory( cur_category );

if(is_new){
notif_text = "Notă salvată" ;
db.insertNote( ext_note );
}else{
notif_text = "Notă actualizată" ;
db.updateNote( ext_note );
}

Snackbar. make(parent_view , notif_text,
Snackbar. LENGTH_SHORT ).setCallback( new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
super.onDismissed(snackbar, event);
finish();
}
}).show() ;
}
}

56
Toate categoriile pot fi vizualizate accesând elementul ”Categorii” din meniu. În
layoutul respectiv se află o listă cu toate categoriile. Fiecare elemen t al listei conține
imaginea categoriei, titlul și numărul de note din acea categorie. Prin selectarea unei
categorii se deschide un alt layout în care se află o listă cu toate notele din categoria
respectivă. De aici se pot modifica, șterge sau adăuga la favorite notele din categorie.

Fig. 9 – Secțiunea de categorii (stânga) și secțiunea pentru afișarea notelor fiecărei
categorii (dreapta).

Tranziția de la o categorie selectată la lista cu notele conținute de aceasta se face
prin intermediul metodei ”getVIew” din activitatea ”ActivityCategoryPick”. Această metodă
primește ca parametrii poziția elementului în listă, un ”View” la care să se facă conversia,
și un ”ViewGroup” părinte. Deoarece activitatea extinde clasa ” AppCompatActivity ” este
necesară, printre altele implementarea acestei metode. Pentru a se crea view-ul la care
se face conversia se utilizează metoda ”inflate” a clasei ”LayoutInflater”, iar imaginea și
titlul categoriei se păstrează într -un ”ViewHolder”, ce are rolul de a descrie view-ul unui
element și date suplimentare despre poziția sa în cadrul ”RecycleView”. Clasa
”LayoutInflater”este folosită pentru a instanția un layout XML în obiectul ”View”
corespunzător.

57

Concluzii

Android este un sistem foarte divers, care a cunoscu o creștere rapidă în ultimii
ani, devenind cel mai utilizat sistem de operare pentru dispozitivele mobile. În legătură
cu dezvoltarea aplicațiilor, Android are un mare avantaj prin folosirea unuia dintre cele
mai populare limbaje de programare. Prin folosirea numeroaselor funcții ale acestuia, se
pot crea aplicații performante cu un aspect plăcut. Prin utilizarea capabilităților unui mediu
de dezvoltare, ca AIDE, aplicațiile se pot realiza mai ușor și mai rapid, iar datorită
documentației bogate și a unei numeroase comunității se pot găsi soluții la orice
problemă.

public View getView( int position, View convertView, ViewGroup parent) {
Category obj = (Category) getItem(position);
ViewHolder holder;
if(convertView == null){
holder = new ViewHolder();
convertView =
LayoutInflater. from(ctx).inflate(R.layout. row_category_simple , parent,
false);
holder.image = (ImageView)
convertView.findViewById(R.id. image);
holder.name = (TextView) convertView.findViewById(R.id. name);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView. getTag();
}

holder.image.setImageResource(obj.getIcon());
((GradientDrawable)
holder.image.getBackground()).setColor(Color. parseColor (obj.getColor()));
holder.name.setText(obj.getName());

return convertView;
}

58
4.Bibliografie
 https://recombu.com/mobile/article/what -is-android -and-what -is-an-
android -phone_M12615.html
 https://dmsp.digital.eca.ed.ac.uk/blog/literaryhighstreet2012/2012/04/27/th
e-impact -of-mobile -technology -on-peoples -live/
 https://en.wikipedia.org/wiki/Android_%28operating_system%29
 http://www.androidcentral.com/android -history
 https://en.wikipedia.org/wi ki/Android_(operating_system
 https://developer.android.com/guide/components/activities.html#Creating
 https://developer.android.com/guide/components/services.html#Lifecycle
 https://developer.android.com/guide/components/fundamentals.html#Com
ponents
 https://developer.android.com/guide/topics/resources/providing –
resources.html#ResourceTypes

 By Smieh – Anatomy Physiology of an Android

Similar Posts