This is an old revision of the document!


Laborator 05 - Intenții

Intenții

Conceptul de intenție în Android este destul de complex (și unic), putând fi definit ca o acțiune având asociată o serie de informații, transmisă sistemului de operare Android pentru a fi executată sub forma unui mesaj asincron. În acest fel, intenția asigură interacțiunea între toate aplicațiile instalate pe dispozitivul mobil, chiar dacă fiecare în parte are o existență autonomă. Din această perspectivă, sistemul de operare Android poate fi privit ca o colecție de componente funcționale, independente și interconectate.

De regulă, o intenție poate fi utilizată pentru:

  •  a invoca activități din cadrul aceleiași aplicații Android;
  •  a invoca alte activități, existente în contextul altor aplicații Android;
  •  a transmite mesaje cu difuzare (eng. broadcast messages), care sunt propagate la nivelul întregului sistem de operare Android și pe care unele aplicații Android le pot prelucra, prin definirea unor clase ascultător specifice; un astfel de comportament este util pentru a implementa aplicații bazate pe evenimente.

O intenție reprezintă o instanță a clasei android.content.Intent. Aceasta este transmisă ca parametru unor metode (de tipul startActivity() sau startService(), definite în clasa abstractă android.content.Context), pentru a invoca anumite componente (activități sau servicii). Un astfel de obiect poate încapsula anumite date (împachetate sub forma unui android.os.Bundle), care pot fi utilizate de componenta ce se dorește a fi executată prin intermediul intenției.

În programarea Android, un principiu de bază este de folosi intenții pentru a propaga acțiuni, chiar și în cadrul aceleiași aplicații, în detrimentul încărcării clasei corespunzătoare. În acest fel, se asigură cuplarea slabă a componentelor, oferind flexibilitate în cazul înlocuirii acestora, permițând totodată extinderea funcționalității cu ușurință.

Controlul fluxului de activități prin intenții

Intenții construite prin precizarea clasei încărcate

În fișierul AndroidManifest.xml, orice activitate definește în cadrul elementului <html><intent-filter></html>, denumirea unei acțiuni care va putea fi folosită de o intenție pentru a o invoca.

<activity
  android:name="org.rosedu.dandroid.lab05.MainActivity"
  android:label="@string/app_name" >
  <intent-filter>
    <action android:name="org.rosedu.dandroid.lab05.intent.action.MainActivity" />
    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

Pentru ca o activitate să poată fi invocată, aceasta trebuie să specifice la elementul category din <intent-filter> valoarea android.intent.category.DEFAULT.

Pentru ca o funcționalitatea expusă de o activitate să poată fi invocată (în mod anonim) și din contextul altor componente ale sistemului de operare Android, pentru tipul de acțiune și pentru tipurile de date precizate, în cadrul secțiunii <html><intent-filter></html> trebuie precizat atributul android:label (șir de caractere care conține o descriere a funcționalității implementate), indicându-se ca tip de categorie valorile ALTERNATIVE, SELECTED_ALTERNATIVE sau ambele.

O activitate este în principiu invocată de o intenție care poate fi identificată prin apelul metodei getIntent(). Rezultatul acestei metode poate fi inclusiv null, în cazul în care activitatea nu a fost pornită prin intermediul unei intenții.

Prin intermediul unei intenții, o aplicație poate invoca atât o activitate din cadrul său, cât și o activitate aparținând altei aplicații.

  • în situația în care se apelează o activitate din cadrul aceleiași aplicații, se poate folosi folosi metoda
    startActivity(new Intent(this, AnotherActivity.class));
  • dacă se dorește rularea unei activități existente în cadrul altei aplicații, aceasta va trebui referită prin numele său complet, inclusiv denumirea pachetului care o identifică
    startActivity(new Intent("org.rosedu.dandroid.lab05.AnotherActivity"));

De remarcat faptul că în situația în care este pornită o activitate din cadrul aceleiași aplicații Android, obiectul de tip Intent primește ca parametru și contextul curent (this), în timp ce în cazul în care este lansată în execuție o activitate din cadrul altei aplicații Android acest parametru este omis.

În momentul în care este invocată metoda startActivity(), activitatea respectivă este lansată în execuție (prin apelul metodelor onCreate(), onStart(), onResume()) și plasată în vârful stivei care conține toate componentele care au rulate anterior, fără a fi fost terminate. În momentul în care se apelează metoda finish() (sau se apasă butonul Back), activitatea este încheiată (prin apelul metodelor onPause(), onStop(), onDestroy()), fiind scoasă din stivă, restaurându-se activitatea anterioară.

Intenții construite prin intermediul unui URI

O intenție poate fi definită și prin intermediul unei acțiuni care se dorește a fi realizată, pentru care pot fi atașate opțional și anumite date. Utilizatorul care folosește un astfel de mecanism nu cunoaște activitatea (sau aplicația Android) care va fi lansată în execuție pentru realizarea acțiunii respective. Pentru a putea îndeplini o astfel de solicitare, sistemul de operare Android trebuie să identifice, la momentul rulării, activitatea care este cea mai adecvată pentru a rezolva acțiunea dorită. În acest fel, pot fi utilizate funcționalități deja implementate în cadrul sistemului de operare Android, fără a cunoaște în prealabil aplicația responsabilă de aceasta.

În cazul în care există mai multe activități care au specificat la elementul action din intent-category aceeași valoare care este transmisă ca parametru constructorului clasei Intent, la execuția intenției în cauză utilizatorului i se va prezenta o listă de opțiuni dintre care poate alege. Dacă la realizarea selecției va fi precizată și opțiunea Use by default for this action, preferințele vor fi salvate astfel încât în continuare vor fi utilizate fără a se mai solicita intervenția utilizatorului în acest sens.

Procesul de rezolvare a unei intenții (eng. intent resolution) se face prin intermediul analizei tuturor ascultătorilor înregistrați pentru a procesa mesaje cu difuzare (eng. broascast receivers).

Cele mai frecvent utilizate acțiuni implicite ale unui obiect de tip Intent sunt:

  • vizualizarea conținutului specificat în secțiunea data asociată intenției, sub forma unui URI, de către aplicații Android diferite, în funcție de schema (protocolul) utilizat (http - navigator, tel - aplicația pentru formarea unui număr de telefon, geo - Google Maps, content - aplicația pentru gestiunea contactelor din agenda telefonică):
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://android.rosedu.org"));
  • căutarea unor informații pe Internet folosind un motor de căutare, termenul căutat fiind indicat în secțiunea extra asociată intenției, fiind identificată prin cheia SearchManager.QUERY:
    Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
    intent.setData(Uri.parse("http://www.google.ro"));
  • invocarea aplicației Android pentru formarea unui număr de telefon (interfața grafică a aplicației Android poate fi populată deja cu numărul de telefon furnizat în secțiunea de date a intenției asociate, în cadrul unui URI); aplicația Android nativă poate normaliza majoritatea schemelor de numere de telefon (cod de țară, prefix, număr de telefon propriu-zis):
    Intent intent = new Intent(Intent.ACTION_DIAL);
  • invocarea aplicației pentru formarea unui număr de telefon și realizarea propriu-zisă a apelului telefonic, folosind valoarea furnizată în secțiunea de date a intenției asociate (în cadrul unui URI); se recomandă să fie folosită pentru activitățile care înlocuiesc aplicația Android nativă pentru formarea unui număr de telefon:
    Intent intent = new Intent(Intent.ACTION_CALL);
    intent.setData(Uri.parse("tel:0214029466"));

Pentru a fi posibil ca aplicația să realizeze un apel telefonic, în fișierul AndroidManifest.xml trebuie specificată explicit permisiunea pentru o astfel de acțiune
<uses-permission android:name=“android.permission.CALL_PHONE”>.

  • vizualizarea unei locații pe hartă pentru care s-au specificat coordonatele GPS
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("geo:44.436877,26.048029?z=100&q=Education"));
  • selectarea unei valori din cadrul furnizorului de conținut indicat în cadrul secțiunii de date asociate intenției, sub forma unui URI; de regulă, este lansată în execuție ca subacvititate, fiind necesar să furnizeze un URI către valoarea care a fost accesată, atunci când este terminată
    Intent intent = new Intent(Intent.ACTION_PICK);
    intent.setData(Uri.parse("content://contacts/people"));

Alte acțiuni implicite utilizate sunt:

  • ACTION_ALL_APPS - lansează în execuție o activitate care afișează toate aplicațiile Android instalate pe dispozitivul mobil; implicit, această acțiune este tratată de aplicația nativă care listează aplicațiile Android în meniul de aplicații din care pot fi rulate de utilizator prin accesarea pictogramei asociate;
  • ACTION_ANSWER - lansează în execuție o activitate care gestionează apelurile primite;
  • ACTION_BUG_REPORT - lansează în execuție o activitate prin intermediul căruia poate fi raportată funcționarea anormală a unei aplicații Android;
  • ACTION_CALL_BUTTON - lansează în execuție o activitate responsabilă cu formarea numărului de telefon; de regulă, o astfel de acțiune este generată în momentul în care utilizatorul accesează un buton dedicat;
  • ACTION_DELETE - lansează în execuție o activitate care permite ștergerea informațiilor specificate în secțiunea data asociată intenției, sub forma unui URI;
  • ACTION_EDIT - lansează în execuție o activitate care permite modificarea informațiilor specificate în secțiunea data asociată intenției, sub forma unui URI;
  • ACTION_INSERT - lansează în execuție o activitate care permite adăugarea unor informații în cursorul specificat în secțiunea de secțiunea data asociată intenției, sub forma unui URI (în cazul în care este rulată ca subactivitate, trebuie să furnizeze URI-ul informațiilor adăugate);
  • ACTION_SEARCH - lansează în execuție o activitate care implementează o activitate de căutare; termenul care este căutat trebuie să fie specificat în secțiunea extra a activității, fiind identificat prin cheia SearchManager.QUERY;
  • ACTION_SEARCH_LONG_PRESS - lansează în execuție o activitate care implementează o activitate de căutare, fiind generată în momentul în care este detectat un eveniment de tip apăsare prelungită a unui buton dedicat (implicit, lansează în execuție o activitate pentru a realiza o căutare vocală);
  • ACTION_SENDTO - lansează în execuție o activitate pentru a transmite anumite informații către un contact indicat în secțiunea data asociată intenției;
  • ACTION_SEND - lansează în execuție o activitate care transmite informațiile conținute în cadrul intenției către un contact care va fi selectat ulterior (aflat pe un alt dispozitiv mobil):
    • tipul MIME trebuie indicat prin intermediul metodei setType();
    • informațiile propriu-zise trebnuie conținute în secțiunea extra asociată intenției, fiind identificate prin cheile EXTRA_TEXT sau EXTRA_STREAM, în funcție de tipul respectiv (pentru aplicațiile de poștă electronică sunt suportate și cheile EXTRA_EMAIL, EXTRA_CC, EXTRA_BCC, EXTRA_SUBJECT).

Totuși, un utilizator nu poate avea garanția că acțiunea pe care o transmite ca parametru al unei intenții va putea fi rezolvată, întrucât există posibilitatea să nu existe nici o activitate asociată acesteia sau ca aplicația ce ar fi putut să o execute să nu fie instalată în contextul sistemului de operare Android. Din acest motiv, o practică curentă este de a verifica dacă o acțiune poate fi rezolvată înainte de a apela metoda startActivity(). Astfel, procesul de gestiune a pachetelor poate fi interogat (prin intermediul metodei resolveActivity()) dacă există o activitate ce poate executa o acțiune și în caz afirmativ, care este aceasta.

Intent applicationIntent = new Intent(...);
PackageManager packageManager = new PackageManager();
ComponentName componentName = applicationIntent .resolveActivity(packageManager);
if (componentName == null) {
  Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://search?q=pname:..."));
  if (marketIntent.resolveActivity(packageManager) != null) {
    startActivity(marketIntent);
  } else {
    Toast.makeText(getApplicationContext(), "Google Play Store is not available", Toast.LENGTH_LONG).show();
  }
} else {
  startActivity(intent);
}

În situația în care nu este identificată nici o activitate asociată acțiunii respective, utilizatorul poate dezactiva componenta grafică asociată până în momentul în care aceasta devine disponibilă, prin descărcarea aplicației Android corespunzătoare din Google Play Store.

Prin intermediul clasei PackageManager poate fi obținută lista tuturor acțiunilor care pot fi realizate pentru un set de date, atașat unei intenții, invocându-se metoda queryIntentActivities().

Procesul de rezolvare a unei intenții pe baza unei acțiuni implică următoarele etape:

  1. se construiește o listă cu toate filtrele de intenții asociate componentelor din aplicațiile Android existente;
  2. sunt eliminate toate filtrele de intenții care nu corespund acțiunii sau categoriei intenției care se dorește a fi rezolvată:
    1. verificările în privința acțiunii sunt realizate numai în situația în care filtrul de intenție specifică o acțiune; sunt eliminate acele filtre de intenții pentru care nici una dintre acțiunile pe care le include nu corespund acțiunii intenției care se dorește a fi rezolvată;
    2. verificările în privința categorie sunt realizate numai în situația în care filtrul de intenție specifică o categorie sau în cazul în care nu specifică nici o categorie, dacă nici intenția care se dorește a fi rezolvată nu include nici o categorie; sunt eliminate acele filtre de intenții care nu includ toate categoriile pe care le conține și intenția care se dorește a fi rezolvată (putând conține totuși și categorii suplimentare);
  3. fiecare parte a URI-ului datelor corespunzătoare intenției care se dorește a fi rezolvată este comparată cu secțiunea data din filtrul de intenție; gazda, autoritatea, tipul MIME, calea, portul, schema), orice neconcordanță conducând la eliminarea acestuia din listă (în situația în care filtrul de intenții nu specifică proprietăți în secțiunea data, acesta va fi considerat compatibil cu intenția care se dorește a fi rezolvată;
  4. în situația în care, ca urmare a acestui proces, există mai multe componente rămase în listă, utilizatorul va trebui să aleagă dintre toate aceste posibilități.

Aplicațiile Android native sunt supuse aceluiași proces în momentul în care se realizează rezolvarea unei intenții ca și aplicațiile Android instalate din alte surse, având aceeiași prioritate și putând fi chiar înlocuite de acestea, dacă definesc filtre de intenții cu aceleași acțiuni / categorii.

În cazul în care o componentă a unei activități este lansată în execuție prin intermediul unei intenții, aceasta trebuie să identifice acțiunea pe care trebuie să o realizeze și datele pe care trebuie sp le proceseze. În acest sens, clasa Intent pune la dispoziție metodele getAction(), getData() și getExtras().

@Override
protected void onCreate(Bundle state) {
  super.onCreate(state);
  setContentView(R.layout.activity_main);
  Intent intent = getIntent();
  if (intent != null) {
    String action = intent.getAction();
    Uri data = intent.getData();
    Bundle extras = intent.getExtras();
  }
}

În anumite situații, o componentă poate primi și alte intenții după ce a fost creată. De fiecare dată, va fi apelată în mod automat metoda onNewIntent():

@Override
public void onNewIntent(Intent newIntent) {
  super.onNewIntent(newIntent);
  // ...
}

O componentă are de asemenea posibilitatea de a transfera responsabilitatea cu privire la gestiunea unei intenții către altă componentă care corespunde criteriilor legate de acțiune și categorie, prin intermediul metodei startNextMatchingActivity():

Intent intent = getIntent();
if (intent != null) {
  startNextMatchingActivity(intent);
}

În acest mod, o componentă poate indica condiții suplimentare cu privire la tratarea unei anumite acțiuni, în situația în care acestea nu pot fi exprimate în cadrul filtrului de intenții, pentru a putea fi luate în considerare în cadrul procesului automat de identificare a componentei care deservește o intenție.

Intenții construite prin intermediul unui URI

De asemenea, un obiect de tip Intent poate fi creat și prin intermediul unui URI care identifică în mod unic o anumită activitate:

Uri uri = Uri.parse("myprotocol://mynamespace/myactivity");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.putExtra("someKey", someValue);
startActivity(intent);

Pentru a putea fi apelată folosind acest mecanism, activitatea va trebui să definească elementul data în cadrul <intent-filter>:

<activity
  android:name="org.rosedu.dandroid.lab05.AnotherActivity"
  android:label="@string/app_name" >
  <intent-filter>
    <action android:name="org.rosedu.dandroid.lab05.intent.action.AnotherActivity" />
    <category android:name="android.intent.category.DEFAULT" />
    <data
      android:scheme="myprotocol"
      android:host="mynamespace" />
  </intent-filter>
</activity>

De remarcat este faptul că în structura URI-ului, partea de după schemă://protocol/ poate conține orice șir de caractere, rolul său fiind strict acela de a respecta forma unui astfel de obiect (estetic), fără a influența în vreo formă funcționalitatea acestuia.

pdm/laboratoare/05.1458511717.txt.gz · Last modified: 2016/03/21 00:08 by alexandru.radovici
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0