Differences

This shows you the differences between two versions of the page.

Link to this comparison view

eim:laboratoare:laborator07 [2016/02/21 14:11]
127.0.0.1 external edit
eim:laboratoare:laborator07 [2021/02/16 16:06] (current)
dragos.niculescu
Line 1: Line 1:
 ====== Laborator 07. Invocarea de Servicii Web prin Protocolul HTTP ====== ====== Laborator 07. Invocarea de Servicii Web prin Protocolul 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.+===== Protocolul ​HTTP =====
  
-**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 [[http://​www.rfc-editor.org/​rfc/​rfc2616.txt|RFC 2616]].+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 [[http://​www.rfc-editor.org/​rfc/​rfc2616.txt|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//​. ​ Principalele concepte cu care lucrează acest protocol sunt //cererea// și //​răspunsul//​. ​
Line 10: Line 12:
  
 {{ :​eim:​laboratoare:​laborator07:​functionare_protocol_http.png?​nolink&​500 }} {{ :​eim:​laboratoare:​laborator07:​functionare_protocol_http.png?​nolink&​500 }}
 +
 +==== 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\\ <​code>​
 +GET /page.html HTTP/1.1
 +Host: www.server.com
 +
 +</​code> ​ |
 +| ''​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\\ <​code>​
 +POST /page.html HTTP/1.1
 +Host: www.server.com
 +attribute1=value1&​...&​attributen=valuen
 +
 +</​code>​ |
 +| ''​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 |
 +
 +<note important>​Capitalizarea este importantă atunci când se precizează denumirea metodei folosite, făcându-se distincție între minuscule și majuscule.</​note>​
 +
 +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
 +
 +===== Mecanisme pentru Comunicația prin HTTP în Android =====
 +
 +==== Clasa HttpURLConnection ====
  
 În Android, comunicația dintre un server web si un client poate fi gestionată prin intermediul clasei [[http://​developer.android.com/​reference/​java/​net/​HttpURLConnection.html|HttpURLConnection]],​ care pune la dispoziție aceleași funcționalități ca în cazul Java SE. În Android, comunicația dintre un server web si un client poate fi gestionată prin intermediul clasei [[http://​developer.android.com/​reference/​java/​net/​HttpURLConnection.html|HttpURLConnection]],​ care pune la dispoziție aceleași funcționalități ca în cazul Java SE.
Line 31: Line 113:
       * ''​setFixedLengthStreamingMode()''​ - atunci când dimensiunea conținutului este cunoscută în prealabil;       * ''​setFixedLengthStreamingMode()''​ - atunci când dimensiunea conținutului este cunoscută în prealabil;
       * ''​setChunkedStreamingMode()''​ - atunci când dimensiunea conținutului nu este cunoscută       * ''​setChunkedStreamingMode()''​ - atunci când dimensiunea conținutului nu este cunoscută
-  - închiderea conexiunii, prin intermediul metodei ''​disconnect()''​.+  - închiderea conexiunii, prin intermediul metodei ''​disconnect()''​, se face de regulă pe clauza ''​finally''​ a unui bloc ''​try-catch''​ pe care sunt gestionate operațiile ce implică comunicația prin rețea; scopul pentru care este utilizată aceasta metodă este dat de posibilitatea de reutilizare a resurselor folosite de conexiune (sockeți TCP), dacă proprietatea ''​http.keepAlive''​ nu specifică altfel.
  
 <​note>​Implicit,​ clasa ''​HttpURLConnection''​ folosește metoda ''​GET''​. Metoda ''​POST''​ este utilizată numai în situația în care a fost apelată în prealabil metoda ''​setDoOutput(true)''​. <​note>​Implicit,​ clasa ''​HttpURLConnection''​ folosește metoda ''​GET''​. Metoda ''​POST''​ este utilizată numai în situația în care a fost apelată în prealabil metoda ''​setDoOutput(true)''​.
Line 38: Line 120:
 </​note>​ </​note>​
  
-Verificarea existenței unui cuvânt cheie într-o pagină Internet poate fi realizată astfel: 
 <code java> <code java>
-private class WebPageKeywordSearchThread ​extends ​Thread ​{+private class WebPageKeywordSearchAsyncTask ​extends ​AsyncTask<​String,​ Void, String> ​{
  
   @Override   @Override
-  ​public void run() {+  ​protected String doInBackground(String... params) {
     HttpURLConnection httpURLConnection = null;     HttpURLConnection httpURLConnection = null;
-    String ​errorMessage ​= null;+    ​StringBuilder result = new StringBuilder();​ 
 +    ​String ​error = null;
     try {     try {
-      String webPageAddress = webPageAddressEditText.getText().toString()+      String webPageAddress = params[0]
-      String keyword = keywordEditText.getText().toString();+      String keyword = params[1];
       if (webPageAddress == null || webPageAddress.isEmpty()) {       if (webPageAddress == null || webPageAddress.isEmpty()) {
-        ​errorMessage ​= "Web Page address cannot be empty";​+        ​error = "Web Page address cannot be empty";​
       }       }
       if (keyword == null || keyword.isEmpty()) {       if (keyword == null || keyword.isEmpty()) {
-        ​errorMessage ​= "​Keyword cannot be empty";​+        ​error = "​Keyword cannot be empty";​
       }       }
-      if (errorMessage ​!= null) { +      if (error != null) { 
-        final String finalizedErrorMessage = errorMessage;​ +        return ​error;
-        resultsTextView.post(new Runnable() { +
-          @Override +
-          public void run() { +
-            resultsTextView.setText(finalizedErrorMessage);​ +
-          } +
-        }); +
-        return;+
       }       }
       URL url = new URL(webPageAddress);​       URL url = new URL(webPageAddress);​
-      ​StringBuffer resultsTextViewContent = new StringBuffer();​ +      ​result.append("​Protocol:​ " + url.getProtocol() + "​\n"​);​ 
-      resultsTextViewContent.append("​Protocol:​ " + url.getProtocol() + "​\n"​);​ +      ​result.append("​Host:​ " + url.getHost() + "​\n"​);​ 
-      ​resultsTextViewContent.append("​Host:​ " + url.getHost() + "​\n"​);​ +      ​result.append("​Port:​ " + url.getPort() + "​\n"​);​ 
-      ​resultsTextViewContent.append("​Port:​ " + url.getPort() + "​\n"​);​ +      ​result.append("​File:​ " + url.getFile() + "​\n"​);​ 
-      ​resultsTextViewContent.append("​File:​ " + url.getFile() + "​\n"​);​ +      ​result.append("​Reference:​ " + url.getRef() + "​\n"​);​ 
-      ​resultsTextViewContent.append("​Reference:​ " + url.getRef() + "​\n"​);​ +      ​result.append("​==========\n"​);​
-      ​resultsTextViewContent.append("​==========\n"​);​+
       URLConnection urlConnection = url.openConnection();​       URLConnection urlConnection = url.openConnection();​
       if (urlConnection instanceof HttpURLConnection) {       if (urlConnection instanceof HttpURLConnection) {
Line 82: Line 156:
           currentLineNumber++;​           currentLineNumber++;​
           if (currentLineContent.contains(keyword)) {           if (currentLineContent.contains(keyword)) {
-            ​resultsTextViewContent.append("​line:​ "​+currentLineNumber+"​ / "​+currentLineContent+"​\n"​);​+            ​result.append("​line:​ " + currentLineNumber + " / " + currentLineContent+"​\n"​);​
             numberOfOccurrencies++;​             numberOfOccurrencies++;​
           }           }
         }         }
-        ​resultsTextViewContent.append("​Number of occurrencies:​ "​+numberOfOccurrencies+"​\n"​);​ +        ​result.append("​Number of occurrencies:​ " + numberOfOccurrencies+"​\n"​);​ 
-        ​final String finalizedResultsTextViewContent = resultsTextViewContent.toString(); +        ​return result.toString();​
-        resultsTextView.post(new Runnable() { +
-          @Override +
-          public void run() { +
-            resultsTextView.setText(finalizedResultsTextViewContent);​ +
-          } +
-        });+
       }       }
     } catch (MalformedURLException malformedURLException) {     } catch (MalformedURLException malformedURLException) {
Line 110: Line 178:
       }       }
     }     }
 +    return null;
   }   }
-} + 
 +  @Override 
 +  public void onPostExecute(String result) { 
 +    resultsTextView.setText(result);​ 
 +  } 
 + 
 +}
 </​code>​ </​code>​
  
-{{ :​eim:​laboratoare:​laborator07:​webpagekeywordsearch.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​01webpagekeywordsearch.png?​nolink&​400 }}
  
 <note warning>​Comunicația dintre serverul web și client trebuie să se realizeze pe un fir de execuție separat, în caz contrar fiind generată excepția ''​android.os.NetworkOnMainThreadException''​.</​note>​ <note warning>​Comunicația dintre serverul web și client trebuie să se realizeze pe un fir de execuție separat, în caz contrar fiind generată excepția ''​android.os.NetworkOnMainThreadException''​.</​note>​
Line 122: Line 197:
 O practică curentă este de a verifica codul de răspuns transmis de a accesa fluxul de intrare asociat resursei respective. Astfel, numai în situația în care valoarea întoarsă de metoda ''​getResponseCode()''​ este ''​HttpURLConnection.HTTP_OK''​ se poate continua procesarea conținutului stocat la URL-ul accesat. O practică curentă este de a verifica codul de răspuns transmis de a accesa fluxul de intrare asociat resursei respective. Astfel, numai în situația în care valoarea întoarsă de metoda ''​getResponseCode()''​ este ''​HttpURLConnection.HTTP_OK''​ se poate continua procesarea conținutului stocat la URL-ul accesat.
  
-===== Cereri ​HTTP =====+==== Apache ​HTTP Components ​====
  
-O cerere ​HTTP conține una sau mai multe linii de text ASCIIprecedate în mod necesar de denumirea metodei specificând operația ce se va realiza asupra conținutului respectiv:+[[https://​hc.apache.org/​|Apache ​HTTP Components]] este un proiect open-source,​ dezvoltat sub licență Apachepunând la dispoziția utilizatorilor o bibliotecă Java pentru accesarea de resurse prin intermediul protocolului HTTP. Funcționalitatea poate fi utilizată în cadrul oricărei mașini virtuale Java și era inclusă și în platforma Android până în API Level 23 (Marshmellow) [[http://​developer.android.com/​about/​versions/​marshmallow/​android-6.0-changes.html#​behavior-apache-http-client|când a fost exclusă]], fiind invocate probleme legate de compatibilitate pentru anumite platforme precum și utilizarea excesivă a rețelei cu impact asupra consumului de energie. În schimb, se recomandă utilizarea clasei HttpURLConnection care asigură compresia datelor (în mod transparent pentru utilizator!) precum și folosirea unui cache. Cu toate acestea, proiectul Apache HTTP Components este în continuă dezvoltare și folosit pe scară largă.
  
- **DENUMIRE METODĂ** ​ ^  **DESCRIERE** ​ ^ +Componentele Apache HTTP Components sunt: 
-**''​GET''​** | descărcarea resursei specificate de pe serverul web pe clientmajoritatea cererilor către un server web sunt de acest tip\\ <​code>​ +  ​* ​[[https://​hc.apache.org/​httpcomponents-core-ga/​index.html|HttpCore]] este un set de componente de transport care pot fi utilizate pentru dezvoltarea de servicii robuste, la nivel de server și client; sunt implementate atât un model blocant pentru operații de intrare/​ieșire (bazat pe ''​java.io''​) cât și un model asincron, bazat pe evenimente (bazat ​pe ''​java.nio''​)
-GET /page.html HTTP/1.1 +  * [[https://​hc.apache.org/​httpcomponents-client-ga/​index.html|HttpClient]] este o implementare a unui agent compatibil cu HTTP/​1.1 ​care oferă funcționalități pentru autentificare la nivel de client, pentru gestiunea stării și a conexiunii; 
-Hostwww.server.com+  * [[https://hc.apache.org/​httpcomponents-asyncclient-dev/​index.html|HttpAsyncClient]] este un modul complementar destinat situațiilor în care se dorește să se ofere suport pentru un număr mare de conexiuni concurente, parametrii precum nivelul de transfer al datelor nu sunt foarte importante.
  
-</​code> ​ | +Pentru ca metodele din API-ul Apache HTTP Components să poată fi utilizate într-o aplicație Android ce utilizează API Level este necesar să se specifice utilizarea bibliotecii corespunzătoare, în fișierul ''​build.gradle'':​
-| ''​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\\ <​code>​ +
-POST /page.html HTTP/1.1 +
-Hostwww.server.com +
-attribute1=value1&​...&​attributen=valuen+
  
-</code| +<file gradle build.gradle
-| ''​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 |+
  
-<note important>​Capitalizarea este importantă atunci când se precizează denumirea metodei folosite, făcându-se distincție între minuscule și majuscule.</​note>​+android { 
 +  compileSdkVersion 23 
 +  buildToolsVersion "23.0.2"
  
-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:​ +  useLibrary ​'org.apache.http.legacy
-  * ''​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+
  
-În Android, conexiunile HTTP sunt gestionate mai ales prin intermediul clasei [[http://​developer.android.com/​reference/​org/​apache/​http/​client/​HttpClient.html|HttpClient]],​ care oferă suport complet pentru acest protocol de comunicație. Acesta este o implementare [[http://​hc.apache.org/​httpcomponents-client-4.4.x/​index.html|Apache Http Components]] care este integrată în SDK, fără a se specifica însă versiunea utilizată. ​+... 
 +</file>
  
-Cel mai frecvent, un obiect de acest tip este folosit pentru transmiterea unor cereri de tip ''​GET''​ ([[http://developer.android.com/reference/​org/​apache/​http/​client/​methods/​HttpGet.html|HttpGet]]) sau ''​POST''​ ([[http://developer.android.com/reference/​org/​apache/​http/​client/methods/HttpPost.html|HttpPost]]) și prelucrarea răspunsurilor corespunzătoare.+Conform API-ului Apache HTTP Components, conexiunile HTTP sunt gestionate ​mai ales prin intermediul clasei ​[[https://hc.apache.org/​httpcomponents-client-ga/​httpclient/apidocs/​org/​apache/​http/​client/​HttpClient.html|HttpClient]], care oferă suport complet pentru acest protocol de comunicație. Frecvent, ca implementare a acestei interfețe se utilizează ​[[https://hc.apache.org/​httpcomponents-client-ga/​httpclient/apidocs/​org/​apache/​http/​impl/client/DefaultHttpClient.html|DefaultHttpClient]].
  
-==== GET vs. POST ==== +Un astfel de obiect poate folosit ​pentru ​transmiterea ​unor cereri ​de tip ''​GET''​ ([[https://​hc.apache.org/​httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​client/​methods/​HttpGet.html|HttpGet]]sau ''​POST''​ ([[https://​hc.apache.org/​httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​client/​methods/​HttpPost.html|HttpPost]]) și prelucrarea răspunsurilor corespunzătoare.
- +
-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''​. +
- +
-==== Cereri de tip GET ====+
  
 Metoda ''​GET''​ este folosită pentru obținerea conținutului unei pagini Internet stocată la o anumită adresă. Metoda ''​GET''​ este folosită pentru obținerea conținutului unei pagini Internet stocată la o anumită adresă.
Line 181: Line 231:
   - instanțierea unui obiect de tipul ''​HttpGet''​ având ca parametru adresa serverului web ce conține resursa care se dorește a fi descărcată;​   - instanțierea unui obiect de tipul ''​HttpGet''​ având ca parametru adresa serverului web ce conține resursa care se dorește a fi descărcată;​
   - realizarea propriu-zisă a cererii HTTP prin apelul metodei ''​execute()''​ a obiectului ''​HttpClient'',​ ce primește ca parametru obiectul de tip ''​HttpGet''​ (încapsulând tipul metodei folosite și locația care va fi interogată).   - realizarea propriu-zisă a cererii HTTP prin apelul metodei ''​execute()''​ a obiectului ''​HttpClient'',​ ce primește ca parametru obiectul de tip ''​HttpGet''​ (încapsulând tipul metodei folosite și locația care va fi interogată).
- 
-=== Exemplu utilizare GET === 
  
 <code java> <code java>
Line 200: Line 248:
   }   }
 } }
-</​code> ​ +</​code>​
- +
-==== ====+
  
-Alternativ, poate fi utilizat un obiect de tip [[http://developer.android.com/reference/​org/​apache/​http/​client/​ResponseHandler.html|ResponseHandler]] care va fi transmis ca parametru metodei ''​execute()'',​ astfel încât rezultatul acesteia să fie un șir de caractere conținând resursa care se dorește a fi descărcată.+Alternativ, poate fi utilizat un obiect de tip [[https://hc.apache.org/httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​client/​ResponseHandler.html|ResponseHandler]] ​(cu implementarea [[https://​hc.apache.org/​httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​impl/​client/​BasicResponseHandler.html|BasicResponseHandler]]) ​care va fi transmis ca parametru metodei ''​execute()'',​ astfel încât rezultatul acesteia să fie un șir de caractere conținând resursa care se dorește a fi descărcată.
  
 <code java> <code java>
Line 213: Line 259:
 </​code>​ </​code>​
  
-În situația în care se dorește transmiterea de parametri către serverul web, aceștia trebuie incluși în URL, fără a se depăși limita de 2048 de caractere și folosind numai caractere ASCII:+În situația în care se dorește transmiterea de parametri către serverul web, aceștia trebuie incluși în URL (în clar), fără a se depăși limita de 2048 de caractere și folosind numai caractere ASCII:
  
 <code java> <code java>
Line 220: Line 266:
 // ... // ...
 </​code>​ </​code>​
- 
-==== Cereri de tip POST ==== 
  
 Metoda ''​POST''​ este folosită atunci când se dorește generarea unui conținut de către serverul web pe baza unor informații transmise de client. Aceste date - având forma unei liste de tip (atribut, valoare) - vor fi incluse în conținutul mesajului în loc de a fi integrate în cadrul URL-ului. Metoda ''​POST''​ este folosită atunci când se dorește generarea unui conținut de către serverul web pe baza unor informații transmise de client. Aceste date - având forma unei liste de tip (atribut, valoare) - vor fi incluse în conținutul mesajului în loc de a fi integrate în cadrul URL-ului.
Line 229: Line 273:
   - instanțierea unui obiect de tipul ''​HttpPost''​ având ca parametru adresa serverului web ce conține resursa care se dorește a fi descărcată;​   - instanțierea unui obiect de tipul ''​HttpPost''​ având ca parametru adresa serverului web ce conține resursa care se dorește a fi descărcată;​
   - **definirea unei liste de perechi de tip (atribut, valoare) (de tip ''​List<​NameValuePair>''​) care vor conține informațiile transmise de client pe baza cărora serverul web va genera conținutul**;​   - **definirea unei liste de perechi de tip (atribut, valoare) (de tip ''​List<​NameValuePair>''​) care vor conține informațiile transmise de client pe baza cărora serverul web va genera conținutul**;​
-  - **atașarea datelor respective la obiectul de tip ''​HttpPost''​ (apelând metoda ''​setEntity()''​) prin intermediul unei entități ​''​UrlEncodedFormEntity'' ​care primește ca parametru informațiile ce trebuie transmise și mecanismul de codificare a datelor**;+  - **atașarea datelor respective la obiectul de tip ''​HttpPost''​ (apelând metoda ''​setEntity()''​) prin intermediul unei entități ​[[https://​hc.apache.org/​httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​client/​entity/​UrlEncodedFormEntity.html|UrlEncodedFormEntity]] ​care primește ca parametru informațiile ce trebuie transmise și mecanismul de codificare a datelor**;
   - realizarea propriu-zisă a cererii HTTP prin apelul metodei ''​execute()''​ a obiectului ''​HttpClient'',​ ce primește ca parametru obiectul de tip ''​HttpPost''​ (încapsulând tipul metodei folosite, datele care se doresc a fi transmise și locația care va fi interogată).   - realizarea propriu-zisă a cererii HTTP prin apelul metodei ''​execute()''​ a obiectului ''​HttpClient'',​ ce primește ca parametru obiectul de tip ''​HttpPost''​ (încapsulând tipul metodei folosite, datele care se doresc a fi transmise și locația care va fi interogată).
  
-=== Exemplu utilizare POST === 
  
 <code java> <code java>
Line 254: Line 297:
    }    }
 } catch (Exception exception) { } catch (Exception exception) {
-    ​Log.e(Constants.TAG,​ exception.getMessage());​ +  ​Log.e(Constants.TAG,​ exception.getMessage());​ 
-    if (Constants.DEBUG) { +  if (Constants.DEBUG) { 
-      exception.printStackTrace();​ +    exception.printStackTrace();​ 
-    }+  }
 } }
 </​code>​ </​code>​
  
-===== Răspunsuri HTTP ===== +Prelucrarea ​unui răspuns HTTP se poate realiza: 
- +  * prin prelucrarea obiectului de tip [[https://​hc.apache.org/​httpcomponents-client-ga/​httpclient/​apidocs/​org/​apache/​http/​client/​entity/​UrlEncodedFormEntity.html|HttpEntity]], utilizând fluxuri de intrare/​ieșire:​\\ <code java> 
-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. +BufferedReader bufferedReader ​= null;
- +
-Î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 +
- +
-În Android, prelucrarea ​unui răspuns HTTP se poate realiza: +
-  * prin prelucrarea obiectului de tip ''​HttpEntity''​, utilizând fluxuri de intrare/​ieșire:​\\ <code java> +
-BufferedReader bufferedReader;​+
 StringBuilder result = new StringBuilder();​ StringBuilder result = new StringBuilder();​
 try { try {
Line 297: Line 315:
   while ((currentLineContent = bufferedReader.readLine()) != null) {   while ((currentLineContent = bufferedReader.readLine()) != null) {
     currentLineNumber++;​     currentLineNumber++;​
-    result.append(currentLineNumber ​": " ​currentLineContent ​"​\n"​);​+    result.append(currentLineNumber).append(": ").append(currentLineContent).append("​\n"​);​
   }   }
   Log.i(Constants.TAG,​ result.toString());​   Log.i(Constants.TAG,​ result.toString());​
Line 319: Line 337:
 </​code> ​ </​code> ​
   * utilizând un obiect de tip ''​ResponseHandler'',​ ce furnizează conținutul resursei solicitate, transmis ca parametru al metodei ''​execute()''​ a clasei ''​HttpClient''​ (pe lângă obiectul ''​HttpGet|HttpPost''​)   * utilizând un obiect de tip ''​ResponseHandler'',​ ce furnizează conținutul resursei solicitate, transmis ca parametru al metodei ''​execute()''​ a clasei ''​HttpClient''​ (pe lângă obiectul ''​HttpGet|HttpPost''​)
 +
 +==== Volley ====
 +<​spoiler>​
 +[[http://​developer.android.com/​training/​volley/​index.html|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:
 +  - gestiune automată a cererilor (implementare sistem de priorități,​ posibilitate de manipulare a cererilor concurente, funcționalitate pentru anulare a unei cereri transmise anterior);
 +  - utilizarea transparentă a unui sistem de caching implementat atât în memorie cât și pe discul local;
 +  - posibilitatea de implementare a unor mecanisme specifice pentru retransmitere;​
 +  - implementarea de operații de tip RPC (Remote Procedure Call) executate asincron pentru popularea conținutului unor elemente din interfața grafică;
 +  - 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:
 +  - descărcarea codului sursă de la depozitul unde este întreținută <​code>​
 +student@eim-lab:​~$ git clone https://​www.github.com/​google/​volley
 +</​code>​
 +  - referirea proiectului ca bibliotecă în Android Studio
 +    * //File// -> //New// -> //Import Module...// {{ :​eim:​laboratoare:​laborator07:​02volleyandroidstudio.png?​nolink }}
 +    * 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 <file gradle 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'​)
 +}
 +</​file>​
 +    * 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. <file gradle build.gradle>​
 +android {
 +  ...
 +  useLibrary '​org.apache.http.legacy'​
 +  ...
 +}
 +</​file>​
 +
 +Volley gestionează o structură de date [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​RequestQueue.java|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:
 +    - ''​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;
 +    - ''​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.
 +
 +<file java VolleyController.java>​
 +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);​
 +  }
 +
 +}
 +</​file>​
 +
 +<note important>​
 +Contextul transmis ca argument metodei ''​newRequestQueue()''​ trebuie să se refere la contextul aplicației,​ de accea se utilizează metoda ''​getApplicationContext()''​.
 +</​note>​
 +
 +Volley folosește mai multe fire de execuție:
 +  - 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);
 +  - 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;
 +  - 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:​
 +  - firul de execuție principal plasează o cerere în coadă
 +  - firul de execuție responsabil cu implementarea mecanismului de caching verifică dacă există un răspuns pentru cerere
 +    - dacă răspunsul pentru cerere se află în cache
 +      - se citește răspunsul din cache
 +      - se scrie rezultatul către firul de execuție principal
 +    - dacă răspunsul pentru cerere NU se află în cache
 +      - se transmite cerere în coadă
 +      - un fir de execuție care realizează comunicația prin rețea trimite cererea, atunci când devine disponibil, preluând răspunsul
 +        - răspunsul este stocat în cache, după ce poate suferi procesări ulterioare (parsare, codificare/​decodificare)
 +        - 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:
 +  - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​StringRequest.java|StringRequest]] furnizează un rezultat de tip șir de caractere, în orice format, folosind un mecanism de codificare;
 +  - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​ImageRequest.java|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;
 +    - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​ImageLoader.java|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;
 +    - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​NetworkImageView.java|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.
 +  - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​JsonRequest.java|JsonRequest]] suportă două implementări:​
 +    - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​JsonObjectRequest.java|JsonObjectRequest]]
 +    - [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​toolbox/​JsonArrayRequest.java|JsonArrayRequest]]
 +
 +Î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 [[https://​android.googlesource.com/​platform/​frameworks/​volley/​+/​master/​src/​main/​java/​com/​android/​volley/​Request.java|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.
 +
 +<file java CustomJSONRequest.java>​
 +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);​
 +  }
 +
 +}
 +</​file>​
 +
 +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)''​.
 +
 +<code java>
 +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);​
 +</​code>​
 +
 +</​spoiler>​
  
 ===== Prelucrare DOM (Document Object Model) ===== ===== Prelucrare DOM (Document Object Model) =====
Line 434: Line 689:
  
 <code java> <code java>
-private class OcwCoursesDisplayerThread ​extends ​Thread ​{+private class OCWCoursesDisplayerAsyncTask ​extends ​AsyncTask<​Void,​ Void, List<​OCWCourseInformation>> ​{ 
   @Override   @Override
-  ​public void run() { +  ​protected List<OCWCourseInformationdoInBackground(Void... params{
-    ArrayList<OcwCourseInformation>​ ocwCourseInformationList = new ArrayList<​OcwCourseInformation>()+
-    ​+
     HttpClient httpClient = new DefaultHttpClient();​     HttpClient httpClient = new DefaultHttpClient();​
-    HttpGet ​httpWebPageGet ​= new HttpGet(Constants.OCW_BASE_INTERNET_ADDRESS + Constants.OCW_REFERRENCE_INTERNET_ADDRESS);+    HttpGet ​httpGet ​= new HttpGet(Constants.OCW_BASE_INTERNET_ADDRESS + Constants.OCW_REFFERRENCE_INTERNET_ADDRESS);
     ResponseHandler<​String>​ responseHandler = new BasicResponseHandler();​     ResponseHandler<​String>​ responseHandler = new BasicResponseHandler();​
 +    List<​String>​ logoAddressesList = new ArrayList<>​();​
 +    int logoAddressesListSize = -1;
 +    List<​String>​ coursesNameList = new ArrayList();​
 +    int coursesNameListSize = -1;
 +    List<​OCWCourseInformation>​ ocwCourseInformationList = new ArrayList<>​();​
 +    int courseInformationListSize = -1;
     try {     try {
-      String pageSourceCode = httpClient.execute(httpWebPageGet, responseHandler);​+      String pageSourceCode = httpClient.execute(httpGet, responseHandler);​
       Document document = Jsoup.parse(pageSourceCode);​       Document document = Jsoup.parse(pageSourceCode);​
       Element htmlTag = document.child(0);​       Element htmlTag = document.child(0);​
-      Elements ​imgTagsClassMediaright ​= htmlTag.getElementsByAttributeValue(Constants.CLASS_ATTRIBUTE,​ Constants.MEDIARIGHT_VALUE); +      Elements ​imgTagsClassMediaRight ​= htmlTag.getElementsByAttributeValue(Constants.CLASS_ATTRIBUTE,​ Constants.MEDIA_RIGHT_VALUE); 
-      Iterator<​Element>​ logoIterator = imgTagsClassMediaright.iterator();​+      Iterator<​Element>​ logoIterator = imgTagsClassMediaRight.iterator();​
       while (logoIterator.hasNext()) {       while (logoIterator.hasNext()) {
         Element imgTagClassMediaRight = logoIterator.next();​         Element imgTagClassMediaRight = logoIterator.next();​
-        ​String logoAddress = Constants.OCW_BASE_INTERNET_ADDRESS + imgTagClassMediaRight.attr(Constants.SRC_ATTRIBUTE)+        ​logoAddressesList.add(Constants.OCW_BASE_INTERNET_ADDRESS + imgTagClassMediaRight.attr(Constants.SRC_ATTRIBUTE));​
-        HttpGet httpLogoGet = new HttpGet(logoAddress);​ +
-        HttpResponse httpResponse = httpClient.execute(httpLogoGet);​ +
-        HttpEntity httpEntity = httpResponse.getEntity();​ +
-         +
-        OcwCourseInformation ocwCourseInformation = new OcwCourseInformation();​ +
-        Bitmap logo = Bitmap.createScaledBitmap(BitmapFactory.decodeStream(httpEntity.getContent()),​ +
-                                                Constants.LOGO_WIDTH,​ +
-                                                Constants.LOGO_HEIGHT,​ +
-                                                false);  +
-        ocwCourseInformation.setLogo(Utilities.makeTransparent(logo));​ +
-        ocwCourseInformationList.add(ocwCourseInformation);+
       }       }
       Elements strongTags = htmlTag.getElementsByTag(Constants.STRONG_TAG);​       Elements strongTags = htmlTag.getElementsByTag(Constants.STRONG_TAG);​
       Iterator<​Element>​ nameIterator = strongTags.iterator();​       Iterator<​Element>​ nameIterator = strongTags.iterator();​
-      int currentPosition = 0; 
       while (nameIterator.hasNext()) {       while (nameIterator.hasNext()) {
         Element strongTag = nameIterator.next();​         Element strongTag = nameIterator.next();​
-        ​String name = strongTag.ownText()+        ​coursesNameList.add(strongTag.ownText());​
-         +
-        ocwCourseInformationList.get(currentPosition++).setName(name);+
       }       }
-       +      ​logoAddressesListSize = logoAddressesList.size();​ 
-      ​final OcwCourseInformationAdapter ocwCourseInformationAdapter ​new OcwCourseInformationAdapter(OcwCoursesDisplayerActivity.this, ocwCourseInformationList); +      ​coursesNameListSize ​coursesNameList.size(); 
-      ​ocwCoursesListView.post(new Runnable() { +      ​if (logoAddressesListSize == coursesNameListSize) { 
-        ​@Override +        ​courseInformationListSize = logoAddressesListSize;​ // = coursesNameListSize 
-        ​public void run() { +        ​for (int index = 0; index < courseInformationListSize;​ index++) { 
-          ​ocwCoursesListView.setAdapter(ocwCourseInformationAdapter);+          ​OCWCourseInformation ocwCourseInformation = new OCWCourseInformation(logoAddressesList.get(index),​ coursesNameList.get(index));​ 
 +          ocwCourseInformationList.add(ocwCourseInformation);
         }         }
-      });+        return ocwCourseInformationList; 
 +      }
     } catch (ClientProtocolException clientProtocolException) {     } catch (ClientProtocolException clientProtocolException) {
       Log.e(Constants.TAG,​ clientProtocolException.getMessage());​       Log.e(Constants.TAG,​ clientProtocolException.getMessage());​
Line 490: Line 738:
         ioException.printStackTrace();​         ioException.printStackTrace();​
       }       }
 +    }
 +    return null;
 +  }
 +
 +  @Override
 +  public void onPostExecute(List<​OCWCourseInformation>​ ocwCourseInformationList) {
 +    for (final OCWCourseInformation ocwCourseInformation:​ ocwCourseInformationList) {
 +      ImageRequest profilePhotoRequest = new ImageRequest(
 +        ocwCourseInformation.getLogoLocation(),​
 +        new Response.Listener<​Bitmap>​() {
 +          @Override
 +          public void onResponse(Bitmap bitmap) {
 +            ocwCourseContentList.add(new OCWCourseContent(bitmap,​ ocwCourseInformation.getName()));​
 +            ocwCourseListAdapter.notifyDataSetChanged();​
 +          }
 +        },
 +        Constants.LOGO_WIDTH,​
 +        Constants.LOGO_HEIGHT,​
 +        null,
 +        Bitmap.Config.RGB_565,​
 +        new Response.ErrorListener() {
 +          public void onErrorResponse(VolleyError volleyError) {
 +            Log.d(Constants.TAG,​ volleyError.toString());​
 +            if (Constants.DEBUG) {
 +              Toast.makeText(ocwCoursesListView.getContext(),​ ocwCoursesListView.getResources().getString(R.string.an_error_has_occurred),​ Toast.LENGTH_LONG)
 +                   ​.show();​
 +            }
 +          }
 +        }
 +      );
 +      VolleyController.getInstance(ocwCoursesListView.getContext()).addToRequestQueue(profilePhotoRequest);​
     }     }
   }   }
Line 495: Line 774:
 </​code>​ </​code>​
  
-{{ :​eim:​laboratoare:​laborator07:​ocwcoursesdisplayer.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​03ocwcoursesdisplayer.png?​nolink&​400 }}
  
 ===== Prelucrare JSON (JavaScript Object Notation) - opțional ===== ===== Prelucrare JSON (JavaScript Object Notation) - opțional =====
  
-Unele servicii web folosesc [[http://​en.wikipedia.org/​wiki/​JSON|formatul JSON]] pentru transferul de date întrucât, spre diferență de XML care implică numeroase informații suplimentare,​ acesta optimizează cantitatea de date transmise. De asemenea, pentru un utilizator uman este mult mai facil să lucreze cu informații reprezentate într-un astfel de format.+Unele servicii web folosesc [[http://​en.wikipedia.org/​wiki/​JSON|formatul JSON]] 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ă. 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ă.
Line 582: Line 861:
   * data și ora la care s-a înregistrat.   * data și ora la care s-a înregistrat.
  
-{{ :​eim:​laboratoare:​laborator07:​earthquakelister.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​04earthquakelister.png?​nolink&​400 }}
  
 Adresa Internet la care este disponibil rezultatul în format JSON trebuie să specifice în mod necesar informațiile cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator: Adresa Internet la care este disponibil rezultatul în format JSON trebuie să specifice în mod necesar informațiile cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator:
Line 588: Line 867:
   * ''​username''​ - denumirea utilizatorului care deține drepturi de acces asupra acestui serviciu web (în cazul de față, ''​pdsd''​).   * ''​username''​ - denumirea utilizatorului care deține drepturi de acces asupra acestui serviciu web (în cazul de față, ''​pdsd''​).
  
-**Exemplu**. O adresă Internet de la care pot fi accesate rezultate în format JSON este: [[http://​api.geonames.org/​earthquakesJSON?​north=44.1&​south=-9.9&​east=-22.4&​west=55.2&​username=pdsd|]].+**Exemplu**. O adresă Internet de la care pot fi accesate rezultate în format JSON este: [[http://​api.geonames.org/​earthquakesJSON?​north=44.1&​south=-9.9&​east=-22.4&​west=55.2&​username=eim-lab|]].
  
 Rezultatele sunt furnizate în următorul format: Rezultatele sunt furnizate în următorul format:
Line 652: Line 931:
 **1.** În contul Github personal, să se creeze un depozit denumit '​Laborator07'​. Inițial, acesta trebuie să fie gol (nu trebuie să bifați nici adăugarea unui fișier ''​README.md'',​ nici a fișierului ''​.gitignore''​ sau a a fișierului ''​LICENSE''​). **1.** În contul Github personal, să se creeze un depozit denumit '​Laborator07'​. Inițial, acesta trebuie să fie gol (nu trebuie să bifați nici adăugarea unui fișier ''​README.md'',​ nici a fișierului ''​.gitignore''​ sau a a fișierului ''​LICENSE''​).
  
-**2.** Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://​www.github.com/​pdsd2015/​Laborator07|]]. ​+**2.** Să se cloneze în directorul de pe discul local conținutul depozitului la distanță de la [[https://​www.github.com/​eim-lab/​Laborator07|]]. ​
  
 În urma acestei operații, directorul Laborator07 va trebui să se conțină directoarele ''​labtasks'',​ ''​samples''​ și ''​solutions''​. ​ În urma acestei operații, directorul Laborator07 va trebui să se conțină directoarele ''​labtasks'',​ ''​samples''​ și ''​solutions''​. ​
  
 <​code>​ <​code>​
-student@pdsd2015:~$ git clone https://​www.github.com/​pdsd2015/​Laborator07+student@eim-lab:~$ git clone https://​www.github.com/​eim-lab/​Laborator07.git
 </​code>​ </​code>​
  
 **3.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator07'​ de pe contul Github personal. <​code>​ **3.** Să se încarce conținutul descărcat în cadrul depozitului '​Laborator07'​ de pe contul Github personal. <​code>​
-student@pdsd2015:~$ cd Laborator07 +student@eim-lab:~$ cd Laborator07 
-student@pdsd2015:​~/​Laborator07$ git remote add Laborator07_perfectstudent https://​github.com/​perfectstudent/​Laborator07 +student@eim-lab:​~/​Laborator07$ git remote add Laborator07_perfectstudent https://​github.com/​perfectstudent/​Laborator07 
-student@pdsd2015:​~/​Laborator07$ git push Laborator07_perfectstudent master+student@eim-lab:​~/​Laborator07$ git push Laborator07_perfectstudent master
 </​code>​ </​code>​
  
-**4.** Să se importe în mediul integrat de dezvoltare ​Eclipse Luna SR1a (4.4.1) ​proiectul ''​01-CalculatorWebService''​ din directorul ''​labtasks''​.+**4.** Să se importe în mediul integrat de dezvoltare ​Android Studio ​proiectul ''​CalculatorWebService''​ din directorul ''​labtasks''​.
  
-Se cere să se implementeze un calculator, care suportă operațiile de adunare/​scădere/​înmulțire/​împărțire a două numere reale, pe baza rezultatului furnizat de un serviciu web, accesibil prin HTTP, la adresele Internet specificate în interfața ''​Constants'',​ pentru fiecare dintre metodele suportate pentru transmiterea informațiilor:​+https://​ocw.cs.pub.ro/​courses/​_media/​eim/​laboratoare/​laborator07/​calculatorwebservice.zip 
 + 
 +**4a.** Să se verifice în browser funcționarea serverului [[http://​wi-fi.cs.pub.ro/​~dniculescu/​didactic/​eim/​expr/​|serverului]] pentru GET și POST 
 + 
 +**4b.** Să se verifice la linia de comandă functionarea severului  
 +    * <code shell> curl  -X  POST  --data '​operation=times&​t1=9&​t2=2'​ https://​wi-fi.cs.pub.ro/​~dniculescu/​didactic/​eim/​expr/​expr_post.php</​code>​ 
 +    * <code shell> curl  "​https://​wi-fi.cs.pub.ro/​~dniculescu/​didactic/​eim/​expr/​expr_get.php?​operation=times&​t1=9&​t2=2"​ </​code>​  
 + 
 +**4c.** studiați implementarea serverului în php  
 +   
 +**4d.** ​Se cere să se implementeze un calculator, care suportă operațiile de adunare/​scădere/​înmulțire/​împărțire a două numere reale, pe baza rezultatului furnizat de un serviciu web, accesibil prin HTTP, la adresele Internet specificate în interfața ''​Constants'',​ pentru fiecare dintre metodele suportate pentru transmiterea informațiilor:​
   * ''​GET'':​ ''​Constants.GET_WEB_SERVICE_ADDDRESS''​   * ''​GET'':​ ''​Constants.GET_WEB_SERVICE_ADDDRESS''​
   * ''​POST'':​ ''​Constants.POST_WEB_SERVICE_ADDRESS''​   * ''​POST'':​ ''​Constants.POST_WEB_SERVICE_ADDRESS''​
Line 676: Line 965:
 Operația care se dorește a fi efectuată precum și metoda prin care vor fi transmise informațiile către serviciul web vor putea fi selectate prin intermediul unor liste. Operația care se dorește a fi efectuată precum și metoda prin care vor fi transmise informațiile către serviciul web vor putea fi selectate prin intermediul unor liste.
  
-{{ :​eim:​laboratoare:​laborator07:​calculatorwebservice.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​05calculatorwebservice.png?​nolink&​400 }}
  
 Parametrii ce trebuie incluși în cadrul cererii HTTP sunt: Parametrii ce trebuie incluși în cadrul cererii HTTP sunt:
  
 <​code>​ <​code>​
-operation = addition|substraction|multiplication|division +operation = plus|minus|divide|times 
-operator1 ​= număr real +t1 = număr real 
-operator2 ​= număr real+t2 = număr real
 </​code>​ </​code>​
  
Line 712: Line 1001:
 În cadrul unui câmp text va putea fi vizualizat răspunsul HTTP furnizat de serviciul web. În cadrul unui câmp text va putea fi vizualizat răspunsul HTTP furnizat de serviciul web.
  
-**5.** ​ Să se importe în mediul integrat de dezvoltare ​Eclipse Luna SR1a (4.4.1) ​proiectul ''​02-XkcdCartoonDisplayer''​ din directorul ''​labtasks''​.+**5.** ​ Să se importe în mediul integrat de dezvoltare ​Android Studio ​proiectul ''​XkcdCartoonDisplayer''​ din directorul ''​labtasks''​.
  
 Se cere să se implementeze o aplicație Android care afișează, într-un control grafic de tip ''​ImageView'',​ cea mai recentă caricatură de pe pagina Internet [[http://​www.xkcd.com|xkcd]]. De asemenea, va putea fi vizualizat titlul acestei caricaturi, adresa Internet la care aceasta poate fi accesată precum și butoane de legătură către caricatura precedentă,​ respectiv caricatura următoare (în situația în care aceasta există). Se cere să se implementeze o aplicație Android care afișează, într-un control grafic de tip ''​ImageView'',​ cea mai recentă caricatură de pe pagina Internet [[http://​www.xkcd.com|xkcd]]. De asemenea, va putea fi vizualizat titlul acestei caricaturi, adresa Internet la care aceasta poate fi accesată precum și butoane de legătură către caricatura precedentă,​ respectiv caricatura următoare (în situația în care aceasta există).
  
-{{ :​eim:​laboratoare:​laborator07:​xkcdcartoondisplayer.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​06xkcdcartoondisplayer.png?​nolink&​400 }}
  
 Pentru realizarea aplicației,​ vor trebui urmați următorii pași: Pentru realizarea aplicației,​ vor trebui urmați următorii pași:
Line 725: Line 1014:
   * obținerea titlului caricaturii (se poate realiza o căutare după eticheta ''<​div>''​ care deține un atribut ''​id''​ cu valoarea ''​ctitle'',​ folosind metoda [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​getElementsByAttributeValue%28java.lang.String,​%20java.lang.String%29|getElementsByAttributeValue()]]);​ ulterior, se va prelua conținutul acestei etichete prin intermediul metodei [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​ownText()|ownText()]]:​ <code java>   * obținerea titlului caricaturii (se poate realiza o căutare după eticheta ''<​div>''​ care deține un atribut ''​id''​ cu valoarea ''​ctitle'',​ folosind metoda [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​getElementsByAttributeValue%28java.lang.String,​%20java.lang.String%29|getElementsByAttributeValue()]]);​ ulterior, se va prelua conținutul acestei etichete prin intermediul metodei [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​ownText()|ownText()]]:​ <code java>
 Element divTagIdCtitle = htmlTag.getElementsByAttributeValue(Constants.ID_ATTRIBUTE,​ Constants.CTITLE_VALUE).first();​ Element divTagIdCtitle = htmlTag.getElementsByAttributeValue(Constants.ID_ATTRIBUTE,​ Constants.CTITLE_VALUE).first();​
-xkcdCartoonInfo.setCartoonTitle(divTagIdCtitle.ownText());​+xkcdCartoonInformation.setCartoonTitle(divTagIdCtitle.ownText());​
 </​code>​ </​code>​
   * obținerea URL-ului imaginii care trebuie afișată (se poate realiza o căutare după eticheta ''<​div>''​ care deține un atribut ''​id''​ cu valoarea ''​comic'',​ folosind metoda ''​getElementsByAttributeValue()''​);​ aceasta conține la rândul ei un element ''<​img>''​ care poate fi accesat prin intermediul metodei [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​getElementsByTag%28java.lang.String%29|getElementsByTag()]];​ adresa Internet a caricaturii este reprezentată de valoarea atributului ''​src'',​ furnizată de metoda [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​attr%28java.lang.String,​%20java.lang.String%29|attr()]]:​ <code java>   * obținerea URL-ului imaginii care trebuie afișată (se poate realiza o căutare după eticheta ''<​div>''​ care deține un atribut ''​id''​ cu valoarea ''​comic'',​ folosind metoda ''​getElementsByAttributeValue()''​);​ aceasta conține la rândul ei un element ''<​img>''​ care poate fi accesat prin intermediul metodei [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​getElementsByTag%28java.lang.String%29|getElementsByTag()]];​ adresa Internet a caricaturii este reprezentată de valoarea atributului ''​src'',​ furnizată de metoda [[http://​jsoup.org/​apidocs/​org/​jsoup/​nodes/​Element.html#​attr%28java.lang.String,​%20java.lang.String%29|attr()]]:​ <code java>
Line 731: Line 1020:
 String cartoonInternetAddress = divTagIdComic.getElementsByTag(Constants.IMG_TAG).attr(Constants.SRC_ATTRIBUTE);​ String cartoonInternetAddress = divTagIdComic.getElementsByTag(Constants.IMG_TAG).attr(Constants.SRC_ATTRIBUTE);​
 </​code> ​ </​code> ​
-  * obținerea caricaturii,​ pe baza adresei Internet la care este găzduită, se face prin fluxul de intrare corespunzător entității HTTP, care va fi decodificat prin intermediul metodei [[http://​developer.android.com/​reference/​android/​graphics/​BitmapFactory.html#​decodeStream(java.io.InputStream)|BitmapFactory.decodeStream()]],​ care întoarce un obiect de tip [[http://​developer.android.com/​reference/​android/​graphics/​Bitmap.html|Bitmap]];​ asocierea unui astfel de obiect ca sursă a unui control grafic ce tip ''​ImageView''​ se face prin intermediul metodei [[http://​developer.android.com/​reference/​android/​widget/​ImageView.html#​setImageBitmap(android.graphics.Bitmap)|setImageBitmap()]];​+  * obținerea caricaturii,​ pe baza adresei Internet la care este găzduită, se face prin fluxul de intrare corespunzător entității HTTP, care va fi decodificat prin intermediul metodei [[http://​developer.android.com/​reference/​android/​graphics/​BitmapFactory.html#​decodeStream(java.io.InputStream)|BitmapFactory.decodeStream()]],​ care întoarce un obiect de tip [[http://​developer.android.com/​reference/​android/​graphics/​Bitmap.html|Bitmap]];​ asocierea unui astfel de obiect ca sursă a unui control grafic ce tip ''​ImageView''​ se face prin intermediul metodei [[http://​developer.android.com/​reference/​android/​widget/​ImageView.html#​setImageBitmap(android.graphics.Bitmap)|setImageBitmap()]]; ​ 
 +<​spoiler>​ 
 +<code java> 
 +            Element divTagIdCtitle = htmlTag.getElementsByAttributeValue(Constants.ID_ATTRIBUTE,​ Constants.CTITLE_VALUE).first();​ 
 +            xkcdCartoonInformation.setCartoonTitle(divTagIdCtitle.ownText());​ 
 + 
 + 
 +            Element divTagIdComic = htmlTag.getElementsByAttributeValue(Constants.ID_ATTRIBUTE,​ Constants.COMIC_VALUE).first();​ 
 +            String cartoonInternetAddress = divTagIdComic.getElementsByTag(Constants.IMG_TAG).attr(Constants.SRC_ATTRIBUTE);​ 
 +            String cartoonUrl = Constants.HTTP_PROTOCOL + cartoonInternetAddress;​ 
 +            xkcdCartoonInformation.setCartoonUrl(cartoonUrl);​ 
 +</​code> ​            
 +</​spoiler>​ 
 +===== Opțional (request cu Volley) ===== 
 +<​spoiler>​ 
 +  * alternativ, se poate folosi biblioteca open-source Volley, care pune la dispoziție un obiect specializat pentru descărcarea de imagini și încărcarea acestora în controalele din cadrul interfeței grafice (''​ImageRequest''​):​ <code java> 
 +ImageRequest cartoonRequest = new ImageRequest( 
 +  cartoonUrl,​ 
 +  new Response.Listener<​Bitmap>​() { 
 +    @Override 
 +    public void onResponse(Bitmap bitmap) { 
 +      xkcdCartoonImageView.setImageBitmap(bitmap);​ 
 +    } 
 +  }, 
 +  0, 
 +  0, 
 +  null, 
 +  Bitmap.Config.RGB_565,​ 
 +  new Response.ErrorListener() { 
 +    public void onErrorResponse(VolleyError volleyError) { 
 +      Log.d(Constants.TAG,​ volleyError.toString());​ 
 +      if (Constants.DEBUG) { 
 +        Toast.makeText(xkcdCartoonTitleTextView.getContext(),​ xkcdCartoonTitleTextView.getResources().getString(R.string.an_error_has_occurred),​ Toast.LENGTH_LONG) 
 +             ​.show();​ 
 +      } 
 +    } 
 +  } 
 +); 
 +VolleyController.getInstance(xkcdCartoonImageView.getContext()).addToRequestQueue(cartoonRequest);​ 
 +</​code>​ 
 +</​spoiler>​ 
 + 
   * obținerea adreselor Internet corespunzătoare caricaturilor anterioară,​ respectiv următoare, care vor fi asociate obiectului ascultător ce tratează evenimentul de tip apăsare al butonului aferent; se poate realiza o căutare după eticheta ''<​a>''​ care deține un atribut ''​rel''​ cu valoarea ''​prev'',​ respectiv ''​next'',​ folosind metoda ''​getElementsByAttributeValue()''​);​ URL-ul reprezintă valoarea atributului ''​href'',​ furnizată de metoda ''​attr'':​ <code java>   * obținerea adreselor Internet corespunzătoare caricaturilor anterioară,​ respectiv următoare, care vor fi asociate obiectului ascultător ce tratează evenimentul de tip apăsare al butonului aferent; se poate realiza o căutare după eticheta ''<​a>''​ care deține un atribut ''​rel''​ cu valoarea ''​prev'',​ respectiv ''​next'',​ folosind metoda ''​getElementsByAttributeValue()''​);​ URL-ul reprezintă valoarea atributului ''​href'',​ furnizată de metoda ''​attr'':​ <code java>
 Element aTagRelPrev = htmlTag.getElementsByAttributeValue(Constants.REL_ATTRIBUTE,​ Constants.PREVIOUS_VALUE).first();​ Element aTagRelPrev = htmlTag.getElementsByAttributeValue(Constants.REL_ATTRIBUTE,​ Constants.PREVIOUS_VALUE).first();​
Line 761: Line 1092:
  
 <code java> <code java>
-private class XkcdCartoonDisplayerAsyncTask ​extends AsyncTask<​String,​ Void, XkcdCartoonInfo> {+private class XKCDCartoonDisplayerAsyncTask ​extends AsyncTask<​String,​ Void, XKCDCartoonInformation> {
  
   @Override ​   ​   @Override ​   ​
-  protected ​XkcdCartoonInfo ​doInBackground(String... urls) {+  protected ​XKCDCartoonInformation ​doInBackground(String... urls) {
     // get the html document corresponding to the xkcd.com website     // get the html document corresponding to the xkcd.com website
     // parse the document in order to obtain the needed information     // parse the document in order to obtain the needed information
Line 770: Line 1101:
  
   @Override   @Override
-  protected void onPostExecute(XkcdCartoonInfo xkcdCartoonInfo) {+  protected void onPostExecute(XKCDCartoonInformation xkcdCartoonInformation) {
     // get references to the needed information     // get references to the needed information
     // map the information to the corresponding widget ​     // map the information to the corresponding widget ​
Line 782: Line 1113:
  
 Lansarea în execuție a unui obiect de tip ''​AsyncTask''​ se realizează printr-un apel al metodei ''​execute()'':​ Lansarea în execuție a unui obiect de tip ''​AsyncTask''​ se realizează printr-un apel al metodei ''​execute()'':​
 +
  
 <code java> <code java>
-new XkcdCartoonDisplayerAsyncTask().execute(Constants.XKCD_INTERNET_ADDRESS);​+new XKCDCartoonDisplayerAsyncTask().execute(Constants.XKCD_INTERNET_ADDRESS);​
 </​code>​ </​code>​
  
-**6.** Să se importe în mediul integrat de dezvoltare ​Eclipse Luna SR1a (4.4.1) ​proiectul ''​03-GoogleSearcher''​ din directorul ''​labtasks''​.+<note important>​ 
 +Atenție, site-ul xkcd a trecut la https, ceea ce pornește un [[https://​stackoverflow.com/​questions/​29916962/​javax-net-ssl-sslhandshakeexception-javax-net-ssl-sslprotocolexception-ssl-han|bug]] din Android < 5.0, deci pentru rulare trebuie să folosiți o imagine de Android cu versiune mai nouă decât Lollipop. ​  
 +</​note>​ 
 + 
 +   * Nu clickați aici decât dupa ce ați rezolvat exercițiul:​ [[https://​xkcd.com/​1174/​|mandatory recursive reference]] ​ m(  
 + 
 +**6.** Să se importe în mediul integrat de dezvoltare ​Android Studio ​proiectul ''​GoogleSearcher''​ din directorul ''​labtasks''​.
  
 Se cere să se implementeze o aplicație Android care să realizeze o căutare folosind motorul [[http://​www.google.com|Google]],​ rezultatul fiind expus printr-un control grafic de tip [[http://​developer.android.com/​reference/​android/​webkit/​WebView.html|WebView]]. Se cere să se implementeze o aplicație Android care să realizeze o căutare folosind motorul [[http://​www.google.com|Google]],​ rezultatul fiind expus printr-un control grafic de tip [[http://​developer.android.com/​reference/​android/​webkit/​WebView.html|WebView]].
  
-{{ :​eim:​laboratoare:​laborator07:​googlesearcher.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​07googlesearcher.png?​nolink&​400 }}
  
 În situația în care șirul de caractere după care se realizează căutarea conține mai multe cuvinte (având drept delimitator caracterul spațiu), acestea vor fi legate prin intermediul caracterului ''​+''​. În situația în care șirul de caractere după care se realizează căutarea conține mai multe cuvinte (având drept delimitator caracterul spațiu), acestea vor fi legate prin intermediul caracterului ''​+''​.
Line 814: Line 1152:
 </​code>​ </​code>​
  
-**7.** (opțional) Să se importe în mediul integrat de dezvoltare ​Eclipse Luna SR1a (4.4.1) ​proiectul ''​04-LandmarkLister''​ din directorul ''​labtasks''​.+**7.** (opțional) Să se importe în mediul integrat de dezvoltare ​Android Studio ​proiectul ''​LandmarkLister''​ din directorul ''​labtasks''​.
  
-Se cere să se implementeze o aplicație Android care să afișeze informații cu privire la obiectivele turistice dintr-un areal geografic (desemnat prin coordonatele nord, sud, est, vest) pe baza rezultatelor furnizate de serviciul web [[http://http://​api.geonames.org/​citiesJSON|Geonames Paces]].+Se cere să se implementeze o aplicație Android care să afișeze informații cu privire la obiectivele turistice dintr-un areal geografic (desemnat prin coordonatele nord, sud, est, vest) pe baza rezultatelor furnizate de serviciul web [[http://​api.geonames.org/​citiesJSON|Geonames Paces]].
  
 Detaliile care se doresc a fi vizualizate pentru fiecare obiectiv turistic în parte sunt: Detaliile care se doresc a fi vizualizate pentru fiecare obiectiv turistic în parte sunt:
Line 827: Line 1165:
   * codul de țară.   * codul de țară.
  
-{{ :​eim:​laboratoare:​laborator07:​landmarklister.png?​nolink&​400 }}+{{ :​eim:​laboratoare:​laborator07:​08landmarklister.png?​nolink&​400 }}
  
 Adresa Internet la care este disponibil rezultatul în format JSON conține informații cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator: Adresa Internet la care este disponibil rezultatul în format JSON conține informații cu privire la coordonatele punctelor cardinale, precum și cu privire la numele de utilizator:
Line 907: Line 1245:
  
 **8.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator07'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​ **8.** ​ Să se încarce modificările realizate în cadrul depozitului '​Laborator07'​ de pe contul Github personal, folosind un mesaj sugestiv. <​code>​
-student@pdsd2015:​~/​Laborator07$ git add * +student@eim-lab:​~/​Laborator07$ git add * 
-student@pdsd2015:~/Laborator07$ git commit -m "​implemented taks for laboratory 07" +student@eim-lab:~/Laborateclior07$ git commit -m "​implemented taks for laboratory 07" 
-student@pdsd2015:​~/​Laborator07$ git push Laborator07_perfectstudent master+student@eim-lab:​~/​Laborator07$ git push Laborator07_perfectstudent master
 </​code>​ </​code>​
  
Line 928: Line 1266:
 [[http://​en.wikipedia.org/​wiki/​JSON|Formatul JSON]]\\ ​ [[http://​en.wikipedia.org/​wiki/​JSON|Formatul JSON]]\\ ​
 [[http://​developer.android.com/​reference/​org/​json/​JSONObject.html|JSONObject]] [[http://​developer.android.com/​reference/​org/​json/​JSONObject.html|JSONObject]]
 +====Android Developer Fundamentals====
 +[[https://​docs.google.com/​presentation/​d/​176imWmYy31qcCpkkk0mhx66LKrlhrCZPHGfEn9tBZlE/​edit#​slide=id.g116d7d9d49_3_13|Internet Connection]]
eim/laboratoare/laborator07.1456056708.txt.gz · Last modified: 2016/04/11 11:26 (external edit)
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