Laborator 6 - Biblioteci

Fragmente

În laboratoarele precedente ați învățat despre activități, cum funcționează acestea, și cum le putem conecta între ele. Astăzi vom lucra cu Fragmente. În Android, un Fragment este o clasă ce reprezintă o componentă independentă ce se execută în contextul unei activități.

De ce folosim fragmente? Fragmentele au fost introduse începând cu Android 3.0 (API level 11), iar scopul lor este de a facilita crearea de interefețe ce se pot adapta diferitelor tipuri de ecrane.

Mai pe scurt, un fragment poate fi văzut ca o porțiune de interfață ce poate fi refolosită în cadrul mai multor activități sau pentru o poziționare diferită a aceleiași interfețe - dacă folosim alte tipuri de ecrane (exemplu în figura de mai jos).

Ciclul de viață al unui fragment

Deși se execută în cadrul contextului unei activități, fragmentele au propriul lor ciclu de viață, propriile evenimente și pot fi adăugate sau șterse, chiar dacă activitatea de care aparține este în curs de execuție. Ciclul de viață al fragmentului este dependent de cel al activității sale. De aceea, dacă o activitate apelează onPause(), atunci toate fragmentele sale vor avea această stare.

  • Metoda onCreate() este apelată după apelul onCreate() al activității.
  • Metoda onCreateView() este apelată atunci când fragmentul trebuie să creeze interfața grafică.
  • Metoda onActivityCreated() este apelată după metoda onCreateView(), atunci când activitatea a fost creată.
  • Metoda onStart() este apelată odată ce fragmentul devine vizibil.

Atenție: Fragmentele nu moștenesc clasa Context. Pentru a avea acces la activitatea “gazdă”, trebuie să folosiți metoda getActivity()

Crearea unui fragment

Pentru a scrie un fragment, trebuie să creați o clasă care să moștenească clasa Fragment. Așa cum ați văzut în laboratorul despre activități, puteți suprascrie diverse metode (descrise mai sus) care pot modifica un fragment de-a lungul ciclului său de viață.

public class MyFirstFragment extends Fragment {
 
  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
                           Bundle savedInstanceState) {
      // Inflate the layout for this fragment
      return inflater.inflate(R.layout.my_first_fragment, container, false);
  }
}

În exemplul de mai sus am creat o clasă de tip Fragment, în care putem observa că am suprascris metoda onCreateView(). Așa cum am spus mai sus, această metodă este folosită pentru a crea componentele interfeței(din fișierul .xml) corespunzătoare fragmentului.

Parametrii acestei metode sunt următorii:

  • inflater : încărcarea layout-ului (fișierul .xml) ;
  • container : dacă este non null, corespunde unui view “părinte” unde ar trebui integrată interfața fragmentului
  • savedInstanceState : dacă este non-null, fragmentul este reconstruit pe baza unei stări salvate anterior, transmisă prin acest parametru.

Fragmentele sunt de două tipuri: statice(definite o singură dată în fișierul layout - nu pot fi suprimate sau inlocuite) și dinamice. În acest laborator nu vom intra în detalii în ceea ce privește fragmentele statice, însă este important să știți de existența lor.

Comuncații în rețea

Indiferent de protocolul pe care îl folosește pentru a accesa resursele disponibile prin intermediul rețelei, o aplicație Android trebuie să respecte anumite constrângeri:

  1. orice comunicație prin rețea trebuie să se realizeze prin intermediul unui fir de execuție dedicat;
  2. în fișierul AndroidManifest.xml trebuie să se specifice permisiunile necesare pentru a se putea accesa rețeaua de calculatoare.

Specificarea Permisiunilor de Utilizare a Rețelei

O aplicație Android nu are acces la comunicația prin rețea decât prin intermediul unor permisiuni speciale, acestea fiind precizate în fișierul AndroidManifest.xml:

  • android.permission.INTERNET: controlează drepturile de acces referitoare la comunicația prin rețea, chiar și în situația în care aceasta se realizează local sau pentru utilizarea unor servicii de rețea;
  • android.permission.ACCESS_NETWORK_STATE: furnizează informații cu privire la conectivitatea dispozitivului mobil.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="org.rosedu.dandroid.messageme" >
 
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
  <!-- other application properties or components --> 
 
</manifest>

De asemenea, este necesar ca în configurațiile dispozitivului mobil, în secțiunea Wireless & networks să existe activate fie rețelele fără fir (Wi-Fi), fie serviciul de date (Mobile networks). Câteodată unele aplicații sunt restricționate în situația în care au utilizat un anumit trafic sau nu li se permite deloc accesul la rețea. Și aceste configurații trebuie să fie verificate în situația în care operațiile ce implică accesarea unor resurse la distanță nu pot fi realizate.

Comunicația prin HTTP

De multe ori, funcționalitatea pe care o pun la dispoziție aplicațiile Android este preluată din alte surse, datorită limitărilor impuse de capacitatea de procesare și memorie disponibilă ale unui dispozitiv mobil. O strategie posibilă în acest sens este utilizarea HTTP, pentru interogarea unor servicii web, al căror rezultat este de cele mai multe ori oferit în format JSON sau XML. De asemenea, descărcarea unor resurse se poate face prin inspectarea codului sursă al unor pagini Internet (documente HTML), în urma acestei operații detectându-se locația la care acestea sunt disponibile.

HTTP (Hypertext Transfer Protocol) este un protocol de comunicație responsabil cu transferul de hipertext (text structurat ce conține legături) dintre un client (de regulă, un navigator) și un server web, interacțiunea dintre acestea (prin intermediul unei conexiuni TCP persistente pe portul 80) fiind reglementată de RFC 2616. HTTP este un protocol fără stare, pentru persistența informațiilor între accesări fiind necesar să se utilizeze soluții adiacente (cookie, sesiuni, rescrierea URL-urilor, câmpuri ascunse).

Principalele concepte cu care lucrează acest protocol sunt cererea și răspunsul.

  • cererea este transmisă de client către serverul web și reprezintă o solicitare pentru obținerea unor resurse (identificate printr-un URL); aceasta conține denumirea metodei care va fi utilizată pentru transferul de informații, locația de unde se găsește resursa respectivă și versiunea de protocol;
  • răspunsul este transmis de serverul web către client, ca rezultat al solicitării primite, incluzând și o linie de stare (ce conține un cod care indică dacă starea comenzii) precum și alte informații suplimentare

Structura unei Cereri HTTP

O cerere HTTP conține una sau mai multe linii de text ASCII, precedate în mod necesar de denumirea metodei specificând operația ce se va realiza asupra conținutului respectiv:

DENUMIRE METODĂ DESCRIERE
GET descărcarea resursei specificate de pe serverul web pe client; majoritatea cererilor către un server web sunt de acest tip
GET /page.html HTTP/1.1
Host: www.server.com
HEAD obținerea antetului unei pagini Internet, pentru a se verifica parametrii acesteia sau doar pentru a testa corectitudinea unui URL
PUT încărcarea resursei specificate de pe client pe serverul web (cu suprascrierea acesteia, în cazul în care există deja); trebuie specificate și datele de autentificare, utilizatorul respectiv trebuind să aibă permisiunile necesare pentru o astfel de operașie
POST transferul de informații de către client cu privire la resursa specificată, acestea urmând a fi prelucrate de serverul web
POST /page.html HTTP/1.1
Host: www.server.com
attribute1=value1&...&attributen=valuen
DELETE ștergerea resursei specificate de pe serverul web, rezultatul operației depinzând de permisiunile pe care le deține utilizatorul ale cărui date de autentificare au fost transmise în antete
TRACE solicitare de retransmitere a cererii primite de serverul web de la client, pentru a se testa corectitudinea acesteia
CONNECT rezervat pentru o utilizare ulterioară
OPTIONS interogare cu privire la atributele serverului web sau ale unei resurse găzduite de acesta

Capitalizarea este importantă atunci când se precizează denumirea metodei folosite, făcându-se distincție între minuscule și majuscule.

Cel mai frecvent, se utilizează metodele GET (folosită implicit, dacă nu se specifică altfel) și POST.

GET vs. POST

Deși atât metoda GET cât și metoda POST pot fi utilizate pentru descărcarea conținutului unei pagini Internet, transmițând către serverul web valorile unor anumite atribute, între acestea există anumite diferențe:

  • o cerere GET poate fi reținută în cache, fapt ce nu este valabil și pentru o cerere POST;
  • o cerere GET rămâne în istoricul aplicației de navigare, fapt ce nu este valabil și pentru o cerere POST;
  • o cerere GET poate fi reținută printre paginile Internet favorite din cadrul programului de navigare, fapt ce nu este valabil și pentru o cerere POST;
  • o cerere GET impune unele restricții cu privire la lungimea (maxim 2048 caractere) și la tipul de date (doar caractere ASCII) transmise (prin URL), fapt ce nu este valabil și pentru o cerere POST;
  • o cerere GET nu trebuie folosită atunci când sunt implicate informații critice (acestea fiind vizibile în URL), fapt ce nu este valabil și pentru o cerere POST;
  • o cerere GET ar trebui să fie folosită doar pentru obținerea unei resurse, fapt ce nu este valabil și pentru o cerere POST.

O linie de cerere HTTP poate fi succedată de unele informații suplimentare, reprezentând antetele de cerere, acestea având forma atribut:valoare, fiind definite următoarele proprietăți:

  • User-Agent - informații cu privire la browser-ul utilizat și la platforma pe care rulează acesta
  • informații cu privire la conținutul pe care clientul îl dorește de la serverul web, având capacitatea de a-l procesa; dacă serverul poate alege dintre mai multe resurse pe care le găzduiește, va alege pe cele care respectă constrângerile specificate, altfel întoarce un cod de eroare
    • Accept - tipul MIME
    • Accept-Charset - setul de caractere
    • Accept-Encoding - mecanismul de codificare
    • Accept-Language - limba
  • Host (obligatoriu) - denumirea gazdei pe care se găsește resursa (specificată în URL); necesară întrucât o adresă IP poate fi asociată mai multor nume de DNS
  • Authorization - informații de autentificare în cazul unor operații care necesită drepturi privilegiate
  • Cookie - transmite un cookie primit anterior
  • Date - data și ora la care a fost transmisă cererea

Structura unui Răspuns HTTP

Un răspuns HTTP este format din linia de stare, antetele de răspuns și posibile informații suplimentare, conținând o parte sau toată resursa care a fost solicitată de client de pe serverul web.

În cadrul liniei de stare este inclus un cod din trei cifre care indică dacă solicitarea a putut fi îndeplinită sau nu (situație în care este indicată și cauza).

FAMILIE DE CODURI SEMNIFICAȚIE DESCRIERE
1xx Informație răspuns provizoriu, constând din linia de stare și alte antete (fără conținut, terminat de o linie vidă), indicând faptul că cererea a fost primită, procesarea sa fiind încă în desfășurare; nu este utilizată în HTTP/1.0
2xx Succes răspuns ce indică faptul că cererea a fost primită, înțeleasă, acceptată și procesată cu succes
3xx Redirectare răspuns transmis de serverul web ce indică faptul că trebuie realizate acțiuni suplimentare din partea clientului (cu sau fără interacțiunea utilizatorului, în funcție de metoda folosită) pentru ca cererea să poată fi îndeplinită; în cazul în care redirectarea se repetă de mai multe ori, se poate suspecta o buclă infinită
4xx Eroare la client răspuns transmis de serverul web ce indică faptul că cererea nu a putut fi îndeplinită, datorită unei erori la nivelul clientului; mesajul include și o entitate ce conține o descriere a situației, inclusiv tipul acesteia (permanentă sau temporară)
5xx Eroare la server cod de răspuns ce indică clientului faptul că cererea nu a putut fi îndeplinită, datorită unei erori la nivelul serverului web; mesajul include și o entitate ce conține o descriere a situației, inclusiv tipul acesteia (permanentă sau temporară)

Mesajul conține și antetele de răspuns, având forma atribut:valoare, fiind definite următoarele proprietăți:

  • Server - informații cu privire la mașina care găzduiește resursa care este transmisă
  • informații cu privire la proprietățile conținutului care este transmis
    • Content-Encoding - mecanismul de codificare
    • Content-Language - limba
    • Content-Length - dimensiunea
    • Content-Type - tipul MIME
  • Last-Modified - ora și data la care pagina Internet a fost modificată
  • Location - informație prin care serverul web informează clientul de faptul că ar trebui folosit alt URL (resursa a fost mutată sau trebuie accesată o pagină Internet localizată în funcție de anumite preferințe)
  • Accept-Ranges - informație referitoare la transmiterea conținutului solicitat în mai multe părți, corespunzătoare unor intervale de octeți
  • Set-Cookie - transmiterea unui cookie de la serverul web la client, acesta trebuind să fie inclus în antetele ulterioare ale mesajelor schimbate între cele două entități

Volley

Volley este o bibliotecă open-source (disponibilă prin intermediul Android Open-Source Project) ce oferă facilități pentru interogarea de resurse disponibile prin intermediul protocolului HTTP.

Câteva dintre facilitățile puse la dispoziție de această platformă sunt:

  1. gestiune automată a cererilor (implementare sistem de priorități, posibilitate de manipulare a cererilor concurente, funcționalitate pentru anulare a unei cereri transmise anterior);
  2. utilizarea transparentă a unui sistem de caching implementat atât în memorie cât și pe discul local;
  3. posibilitatea de implementare a unor mecanisme specifice pentru retransmitere;
  4. implementarea de operații de tip RPC (Remote Procedure Call) executate asincron pentru popularea conținutului unor elemente din interfața grafică;
  5. utilitare pentru depanare.

Volley nu este adecvat pentru operații de intrare / ieșire intensive tocmai datorită sistemului de caching, conținutul resurselor solicitate fiind menținut în memorie în vederea realizării operației de procesare a acestuia.

Pentru utilizarea bibliotecii Volley este necesar să se realizeze următoarele operații:

  1. descărcarea codului sursă de la depozitul unde este întreținută
    student@dandroid2015:~$ git clone https://android.googlesource.com/platform/frameworks/volley
  2. referirea proiectului ca bibliotecă în Android Studio
    • FileNewImport Module…
    • indicarea locației la care au fost descărcate sursele în prelabil și o denumire a proiectului (tipic, :volley) sub care va fi referit
    • specificarea dependenței față de acest proiect precum și faptul că este necesar ca acesta să fie compilat
      build.gradle
      ...
      dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        testCompile 'junit:junit:4.12'
        compile 'com.android.support:appcompat-v7:23.1.1'
        compile 'com.android.support:design:23.1.1'
        compile project(':volley')
      }
    • dacă pentru Volley se folosește API Level 23, este necesar să se utilizeze biblioteca org.apache.http.legacy întrucât implementarea sa utilizează Apache HTTP Components.

Volley gestionează o structură de date RequestQueue care are rolul de a controla modul în care sunt tratate cererile. Aceasta distribuie cererile care trebuie procesate concurent către firele de execuție care se ocupă de comunicația prin rețea, realizând de asemenea interogarea informațiilor care se regăsest în cache. Există două moduri în care poate fi construit un obiect de tip RequestQueue:

  • Volley.newRequestQueue(Context) - construiește o coadă de cereri folosind implementările standard pentru implementarea mecanismului de cache și a comunicației prin rețea:
    1. DiskBasedCache: pentru fiecare răspuns care a fost solicitat prin intermediul unei cereri se construiește un fișier dedicat, căruia îi corespunde un index stocat în memorie;
    2. BasicNetwork: implementează funcționalitate de transport folosind un client HTTP (tipic, un obiect HttpURLConnection);
  • Volley.newRequestQueue(Cache, Network) permite utilizarea unui mecanism de cache și a unei modalități de transmisie prin rețea ai căror comportament și parametri sunt definiți de utilizator.

O coadă de cereri implementează următoarele metode:

  • add() - transmite o cerere care este inițial verificată de firul de execuție care verifică cache-ul astfel încât dacă răspunsul există stocat, va fi furnizat fără a se mai realiza comunicația prin reșea; în caz contrar, el va fi plasat în coada gestionată de firele de execuție responsabile de comunicația prin rețea, acesta fiind preluat în momentul în care unul dintre acestea devine disponibil;
  • cancel() - anulează o cerere care a fost transmisă anterior, împiedicând să se obțină un răspuns pe baza sa (metoda este invocată pe obiectul Request);
  • cancelAll() - anulează toate cererile care au atașată o anumită etichetă, specificată prin intermediul metodei setTag(); o astfel de abordare poate fi utilă atunci când o activitate nu mai este vizibilă (pe metoda onStop()) astfel încât să nu se mai proceseze răspunsuri care nu pot fi afișate; în acest fel, utilizatorul este scutit să verifice dacă instanța activității există sau nu pe metodele de callback apelate în mod automat în momentul în care resursa solicitată devine disponibilă sau în momentul în care s-a produs o excepție.

Este recomandat ca la nivelul unei aplicații Android care utilizează comunicația prin rețea folosind protocolul HTTP să se utilizeze o singură instanță a unui obiect de tip RequestQueue a cărui durată de viață se întinde pe întreaga perioadă de execuție. Abordarea cea mai adecvată este utilizarea unei clase Singleton care conține un obiect de tip RequestQueue precum și alte funcționalități Volley, dacă este necesar.

package org.rosedu.dandroid.messageme.network;
 
import android.content.Context;
 
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
 
public class VolleyController {
 
  private static VolleyController instance;
  private static Context context;
  private RequestQueue requestQueue;
 
  private VolleyController(Context context) {
    this.context = context;
    this.requestQueue = getRequestQueue();
  }
 
  public static synchronized VolleyController getInstance(Context context) {
    if (instance == null) {
      instance = new VolleyController(context);
    }
    return instance;
  }
 
  public RequestQueue getRequestQueue() {
    if (requestQueue == null) {
      requestQueue = Volley.newRequestQueue(context.getApplicationContext());
    }
    return requestQueue;
  }
 
  public <T> void addToRequestQueue(Request<T> request) {
    getRequestQueue().add(request);
  }
 
}

Contextul transmis ca argument metodei newRequestQueue() trebuie să se refere la contextul aplicației, de accea se utilizează metoda getApplicationContext().

Volley folosește mai multe fire de execuție:

  1. firul de execuție principal (al interfeței grafice), prin intermediul căruia este trimisă o cerere și pe care este primit răspunsul (pentru a fi vizualizat, de regulă, în cadrul interfeței grafice);
  2. firul de execuție responsabil cu implementarea mecanismului de caching, care verifică dacă există un răspuns pentru cerere, astfel încât să nu mai fie necesară comunicația prin rețea; în momentul în care este disponibil un răspuns pentru o cerere, aceasta este indexată tot de acest fir de execuție;
  3. mai multe fire de execuție care realizează comunicația prin rețea, care trimit o cerere și primesc un răspuns pe care îl procesează (operațiile de parsare sau codificare/decodificare necesită resurse și o perioadă de timp considerabilă).

O utilizare tipică Volley (algoritmul pe care îl utilizează această platformă) este următoarea:

  1. firul de execuție principal plasează o cerere în coadă
  2. firul de execuție responsabil cu implementarea mecanismului de caching verifică dacă există un răspuns pentru cerere
    1. dacă răspunsul pentru cerere se află în cache
      1. se citește răspunsul din cache
      2. se scrie rezultatul către firul de execuție principal
    2. dacă răspunsul pentru cerere NU se află în cache
      1. se transmite cerere în coadă
      2. un fir de execuție care realizează comunicația prin rețea trimite cererea, atunci când devine disponibil, preluând răspunsul
        1. răspunsul este stocat în cache, după ce poate suferi procesări ulterioare (parsare, codificare/decodificare)
        2. se scrie rezultatul către firul de execuție principal

Volley definește mai multe tipuri de cerere standard, în funcție de tipul de date pe care îl are resursa care va fi accesată prin intermediul protocolului HTTP:

  1. StringRequest furnizează un rezultat de tip șir de caractere, în orice format, folosind un mecanism de codificare;
  2. ImageRequest întoarce o resursă de tip imagine, disponibilă la o anumită locație (URL), aceasta fiind transmisă sub formă de bitmap decodificat (și posibil redimensionat, dacă se solicită acest lucru); operațiile costisitoare sunt realizate de firele de execuție responsabile de comunicația prin rețea;
    1. ImageLoader oferă funcționalitatea de gestiune a cache-ului, care este stocat în memorie (dacă s-ar fi utilizat discul local, ar fi existat un impact asupra firului de execuție principal), transmițând mai multe resurse la un moment dat;
    2. NetworkImageView reprezintă un tip de element al interfeței grafice, specializat pentru resurse de tip imagine disponibilă la o locație la distanță, accesibilă prin intermediul comunicației în rețea.
  3. JsonRequest suportă două implementări:

În situația în care se dorește implementarea unui tip de cerere definită de utilizator, trebuie să se respecte câteva condiții:

  • clasa trebuie să fie derivată din Request<T>;
  • clasa trebuie să implementeze metodele:
    • parseNetworkResponse(NetworkResponse) care construiește rezultatul (Response.success(), respectiv Response.error()) în funcție de răspunsul primit;
    • deliverResponse(T) care apelează metoda de callback (onResponse(T)) atunci când răspunsul este disponibil.
public class CustomRequest<T> extends Request<T> {
 
  private Map<String, String> params;
  private Response.Listener<T> responseListener;
  private Response.ErrorListener errorListener;
  private Class<T> classType;
 
  public CustomRequest(String url, Map<String, String> params, Response.Listener<T> responseListener, Response.ErrorListener errorListener, Class<T> classType) {
    this(Method.GET, url, params, responseListener, errorListener, classType);
  }
 
  public CustomRequest(int method, String url, Map<String, String> params, Response.Listener<T> responseListener, Response.ErrorListener errorListener, Class<T> classType) {
    super(method, url, errorListener);
    this.params = params;
    this.responseListener = responseListener;
    this.errorListener = errorListener;
    this.classType = classType;
  }
 
  @Override
  protected Map<String, String> getParams() throws AuthFailureError {
    return params;
  }
 
  @Override
  protected Response<T> parseNetworkResponse(NetworkResponse networkResponse) {
    try {
      String response = new String(networkResponse.data, HttpHeaderParser.parseCharset(networkResponse.headers));
      if (classType.equals(JSONObject.class)) {
        return Response.success((T)new JSONObject(response), HttpHeaderParser.parseCacheHeaders(networkResponse));
      }
      if (classType.equals(JSONArray.class)) {
        return Response.success((T)new JSONArray(response), HttpHeaderParser.parseCacheHeaders(networkResponse));
      }
      return null;
    } catch (UnsupportedEncodingException unsupportedEncodingException) {
      return Response.error(new ParseError(unsupportedEncodingException));
    } catch (JSONException jsonException) {
      return Response.error(new ParseError(jsonException));
    }
  }
 
  @Override
  protected void deliverResponse(T jsonObject) {
    responseListener.onResponse(jsonObject);
  }
 
  @Override
  public void deliverError(VolleyError volleyError) {
    errorListener.onErrorResponse(volleyError);
  }
 
}

Pentru definirea unui obiect de tip Request este necesar să se precizeze următoarele elemente:

  • metoda folosită pentru transmiterea cererii, definită de Request.Method;
  • locația (URL-ul) la care se găsește resursa care se dorește să fie accesată prin protocolul HTTP;
  • parametrii cererii;
  • metodele de callback
    • Response.Listener<T>:
      • invocată în mod automat în situația în care răspunsul este disponibil;
      • trebuie să implementeze metoda onResponse(T);
    • ErrorListener:
      • invocată în mod automat în situația în care s-a produs o eroare;
      • trebuie să implementeze metoda onErrorResponse(VolleyError).
Map<String, String> parameters = new HashMap<>();
parameters.put(Constants.USERNAME_ATTRIBUTE, senderUsername);
CustomRequest<JSONArray> customRequest = new CustomRequest(
  Request.Method.POST,
  Constants.USER_LIST_WEB_SERVICE_ADDRESS,
  parameters,
  new Response.Listener<JSONArray>() {
    @Override
    public void onResponse(JSONArray response) {
      try {
        for (int position = 0; position < response.length(); position++) {
          JSONObject user = response.getJSONObject(position);
          final String recipientId = user.get(Constants.USER_ID_ATTRIBUTE).toString();
          final String recipientUsername = user.get(Constants.USERNAME_ATTRIBUTE).toString();
          contact = (RelativeLayout)inflater.inflate(R.layout.contact, null);
          TextView usernameTextView = (TextView)contact.findViewById(R.id.username_text_view);
          usernameTextView.setText(recipientUsername);
          Button writeMessageButton = (Button)contact.findViewById(R.id.write_message_button);
          contactsList.addView(contact);
        }
      } catch (JSONException jsonException) {
        Log.e(Constants.TAG, jsonException.getMessage());
        if (Constants.DEBUG) {
          jsonException.printStackTrace();
        }
      }
    }
  },
  new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError volleyError) {
      Snackbar.make(contactsTextView, getResources().getString(R.string.an_error_has_occurred), Snackbar.LENGTH_LONG)
              .show();
    }
  },
  JSONArray.class
);
VolleyController.getInstance(getActivity().getApplicationContext()).addToRequestQueue(customRequest);

Prelucrarea Rezultatelor întoarse de Servicii Web

Formatul JSON (JavaScript Object Notation)

Unele servicii web folosesc formatul JSON, specificat de RFC 7159, pentru transferul de date întrucât, spre diferență de XML care implică numeroase informații suplimentare, acesta optimizează cantitatea de date implicate. De asemenea, este foarte ușor pentru un utilizator uman să prelucreze informațiile reprezentate într-un astfel de format.

Acest mecanism de reprezentare a datelor a fost utilizat inițial în JavaScript, unde acesta descria literali sau obiecte anonime, folosite o singură dată (fără posibilitatea de a fi reutilizate), informațiile fiind transmise prin intermediul unor șiruri de caractere. Ulterior, a fost preliat pe scară largă.

În principiu, informațiile reprezentate în format JSON au structura unei colecții de perechi de tip (cheie, valoare), fiecare dintre acestea fiind grupate într-o listă de obiecte ordonate. Nu se folosesc denumiri de etichete, utilizându-se doar caracterele , ,, {, }, [ și ].

[
  {
    "attribute1": "value11",
    "attribute2": "value12",
    ...,
    "attributen": "value1n"
  },
  {
    "attribute1": "value21",
    "attribute2": "value22",
    ...,
    "attributen": "value2n"
  },
  ...
  {
    "attribute1": "valuem1",
    "attribute2": "valuem2",
    ...,
    "attributen": "valuemn"
  }  
]

În Android este suportat formatul JSON strict, așa cum este descris pe http://json.org/:

  • denumirile de atribute trebuie să se găsească între ghilimele;
  • valorile de tip șir de caractere trebuie să se găsească între ghilimele (nu sunt permise apostroafele în acest caz).

Potrivit RFC 4627, tipul MIME al documentelor reprezentate în format JSON treuie să fie în mod necesar application/json.

Există numeroase servicii web care își expun funcționalitatea prin intermediul unor documente JSON:

În Android, prelucrarea documentelor reprezentate în format JSON este realizată prin intermediul clasei JSONObject care a fost integrată parțial (cu unele funcționalități lipsă), fără a se specifica clar versiunea care este utilizată.

Un document JSON poate fi construit în mai multe moduri:

  • JSONObject() - construiește un obiect JSON vid, care nu conține nici o asociere (atribut, valoare);
  • JSONObject(Map copyFrom) - contruiește un obiect JSON pe baza unor asocieri prealabile; o astfel de metodă este utilizată mai ales în situația în care se dorește să se transmită informații către un serviciu web;
  • JSONObject(String json) - construiește un obiect JSON prin parsarea unui șir de caractere; o astfel de metodă este utilizată mai ales în situația în care sunt primite informații de la un serviciu web, care trebuie să fie prelucrate;
  • JSONObject(JSONTokener readFrom) - construiește un obiect JSON pe baza valorii următoare disponibile în cadrul unui obiect JSONTokener;
  • JSONObject(JSONObject copyFrom, String[] names) - construiește un obiect JSON prin filtrarea asocierilor existente într-un obiect JSONObject, pe baza unor denumiri de atribute existente.

De regulă, metodele care operează cu obiecte JSON pot genera excepția JSONException în situația în care sunt întâlnite documente care nu sunt bine formate.

În Android nu este implementată metoda JSONObject(Bean bean) care permite construirea unui obiect JSON pe baza unei componente Java Bean.

Gestiunea obiectelor JSON se face:

  • pentru citire, prin intermediul metodelor de tip get(String name), care furnizează un obiect generic (de tip Object), aceasta fiind supraîncărcată și pentru tipurile boolean, double, int, long, String, JSONObject, JSONArray;
  • pentru scriere, prin intermediul metodelor de tip put(String name, Object value), care furnizeză un rezultat de tip JSONObject, existând și implementări specifice pentru tipurile boolean, double, int, long.

Verificarea faptului că un document JSON deține un anumit atribut se face prin invocarea metodei has(String name). Funcționalitatea inversă este furnizată de metoda isNull(String name), aceasta verificând și dacă valoarea asociată atributului respectiv este null.

Dimensiunea unui obiect JSONObject, reprezentat sub forma numărului de asocieri de tip (cheie, valoare) este furnizat de metoda length().

Reprezentarea unui obiect JSON sub forma unui șir de caractere poate fi obținută prin intermediul metodelor:

Unele metode furnizează vectori de obiecte JSON, reprezentate de clasa JSONArray, în care accesul la obiecte se face indexat, începând cu 0:

Exercitii

  1. Modificati laboratoarele anterioare astfel incat sa folositi ButterKnife
  2. Afisati pe harta (Google Maps) unde este facultatea Google Maps
  3. Afisati data si imaginea cele mai recente imagini facute de NASA casei voastre https://api.nasa.gov/api.html#earth
pdm/laboratoare/06.txt · Last modified: 2016/03/30 11:04 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