În Tips & Tricks

Integrarea aplicațiilor la nivel de date – partea a II-a

Android apps

În articolul precendent am discutat despre integrarea la nivel de date și am văzut cum putem utiliza content providerii în acest sens. În articolul actual vom vedeam cum putem nu doar să folosim, dar și să creăm provideri dedicați pentru a permite integrarea cu alte aplicații, atât proprii cât și third-party.

După cum menționam în articolul precedent, content providerii administrează accesul la un set de date structurat. Aceștia se ocupă de încapsularea datelor și oferă mecanisme de protecție fiind, practic, standardul pentru conectarea datelor între procese.

Înainte de a începe să dezvoltăm un content provider propriu, trebuie luată decizia dacă el este într-adevăr necesar. După cum spuneam, un content provider este util din punct de vedere al integrării de aplicații. Dacă nu dorim să oferim acces la datele stocate de aplicația noastră sau nu dorim să oferim posibilitatea altor aplicații de a copia informație din aplicația noastră, atunci un content provider nu se justifică, un data abstraction layer cu comunicare directă la baza de date fiind mult mai eficient.

Atunci când începem să construim provider-ul, trebuie știut că el oferă date în două moduri:

1. Fișiere

Aici ne referim în special la accesul la fișiere media (audio, video, photo). Putem stoca fișiere în spațiul privat al aplicației unde accesul la ele este restricționat la aplicația noastră. Provider-ul ne poate ajuta să oferim acces spre exterior la fișierele din spațiul privat.

2. Date structurate

În general, informațiile sunt stocate într-o bază de date, vector sau alte structuri asemănătoare. Despre bazele de date în Android am vorbit într-un articol anterior.

În continuare, asemănător cu alte mecanisme din Android, trebuie să oferim o implementare concretă a clasei Content Provider. Aceasta conține o serie de metode care trebuie suprascrise. Practic, această clasă reprezintă interfața dintre datele noastre și Android.

După implementare, trebuie specificată autoritatea administrată de content provider, URI-urile spre conținut și numele de coloane.

În același timp, vor trebui specificate și drepturile necesare pentru accesul la date. În general, e bine ca aceste valori să fie definite într-o clasă separată ca și constante pentru a putea permite accesul la ea și altor dezvoltatori.

În acest articol nu ne vom referi la proiectarea data store-urilor (fie că e vorba de baze de date sau de fișiere). Acest aspect nu este unul specific platformei și poate constitui un topic de sine stătător.

Ne vom opri în continuare asupra modului de proiectare a content URI-urilor, ele fiind cele care vor identifica data în cadrul unui provider. Ele sunt formate din două părți:

 

  • authority – este o denumire dată numelui simbolic al provider-ului;
  • path – numele care identifică un fișier sau o tabelă.

 

Mai poate conține și al treilea element, care poate fi folosit în cazul bazelor de date pentru a identifica un anume rând în cadrul unei tabele.

Autoritatea

Numele de „autoritate” este cumva pretențios, fiind pur și simplu un nume intern unic pentru un anume provider. În general, este recomandat ca el să respecte regulile de denumire de la pachetele Java sau domeniile WWW. De exemplu, un nume valid de autoritate ar putea fi:

com.example.myapplication.provider

Calea URI Matching

Aceasta este practic calea spre o anumită resursă, fie spre o tabelă, fie spre un fișier.

Un exemplu clasic ar fi:

com.example.myapplication.provider/tablename

Un content provider trebuie să fie capabil să decidă ce date să ofere în funcție de content URI-ul specificat. Pentru aceasta, Android pune la dispoziție o clasă denumită „UriMatcher”, care va mapa unor anumite tipare de content URI, valori întregi care vor putea fi folosite ulterior în cadrul switch-urilor pentru a lua decizii.

Un astfel de tipar poate să conțină două caractere wildcard: * și #. Primul va identifica orice șir de litere de orice lungime, pe când # va identifica orice șir de cifre de orice lungime.

Pentru a folosi un UriMatcher, el trebuie declarat ca și o constantă în content provider și apoi trebuie adăugate tipare de URI-uri precum și valorile întregi care să fie mapate. Acestea sunt cu totul la discreția dezvoltatorului.

De exemplu:

private static final UriMatcher sUriMatcher;
...
sUriMatcher.addURI("com.example.app.provider", "table3", 1);
sUriMatcher.addURI("com.example.app.provider", "table3/#", 2);

Efectul acestui cod este că în cadrul URIMatcher-ului, s-a adăugat o înregistrare care atunci când este primit un request pe autoritatea “com.example.app.provider” pentru calea “table3” acesta va returna “1”. Pentru calea “table3/#” acesta va returna valoarea “2”.

De notat este utilizarea wildcard-lui #. Practic URIMatcher-ul va returna “2” pentru orice request care identifică în mod unic un rând din tabla “table3”.

Modul în care poate fi folosit este următorul:

switch (sUriMatcher.match(uri)) {
    case 1: // tratează cazul în care se dorește accesarea întregii tabele
    break;
    case 2: // tratează cazul în care se dorește accesarea unui rând anume
    break;
    default:
}

Implementarea clasei de Content Provider

Acum că știm cum putem să definim URI-uri pentru content provider, a venit timpul să vedem cum putem să și oferim datele cerute.

După cum spuneam la început, aceasta se face prin implementarea clasei abstracte ContentProvider. Ea conține 6 metode care sunt obligatorii. Patru dintre ele sunt cele care oferă operațiile de bază CRUD (create, retrieve, update, delete) și două de gestionare. Dintre ele, cinci (toate în afara de onCreate0) sunt apelate de clienții care folosesc provider-ul.

Pe scurt, sunt următoarele:

query() – plasează un query și returnează datele sub forma unui Cursor;
insert() – inserează o nouă înregistrare în provider și returnează un content URI spre ea;
update() – modifică o înregistrare existentă și returnează numărul de înregistrări afectate;
delete() – șterge înregistrări existente și returnează numărul de ștergeri efectuate;
getType() – returnează MIME type-ul corespunzător unui provider;
onCreate() – inițializează provider-ul. Această metodă este chemată automat de sistemul Android în momentul creării content provider-ului. Aici este recomandat să fie efectuate doar operații care nu sunt costisitoare din punct de vedere al timpului de procesare, cum ar fi crearea unui obiect helper pentru accesul la baza de date.

Cu cât operațiile de aici sunt mai costisitoare, cu atât timpul de pornire al provider-ului crește, prin urmare și timpul în care aplicațiile primesc informația crește.

Manifest

La fel ca și restul componentelor din Android, Activity și Service, acești provideri trebuie declarați în manifest. Declararea se face analog cu cea de la restul componentelor:

<provider
        android:name="com.example.android.provider.ExampleProvider"
        android:authorities="com.example.android.provider"
...
/>

Name reprezintă numele clasei care implementează content provider-ul, iar authorities este autoritatea care va fi înregistrată de Android în sistem.

Ar mai fi multe de spus despre content provideri, în special despre modul în care putem acorda permisii de acces la anumite resurse, dar informațiile de bază prezentate sunt suficiente pentru a putea implementa un content provider simplu și, pentru moment, ne vom opri aici.