Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
|
laboratoare:laborator07 [2016/04/15 00:28] Andrei Roșu-Cojocaru [Volley] |
laboratoare:laborator07 [2017/02/20 20:54] (current) |
||
|---|---|---|---|
| Line 368: | Line 368: | ||
| } | } | ||
| </file> | </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. | + | * 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 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'': | ||
| Line 384: | Line 390: | ||
| <file java VolleyController.java> | <file java VolleyController.java> | ||
| - | package org.rosedu.dandroid.messageme.network; | ||
| - | |||
| import android.content.Context; | import android.content.Context; | ||
| Line 683: | Line 687: | ||
| <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<OCWCourseInformation> doInBackground(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 739: | Line 736: | ||
| 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 744: | Line 772: | ||
| </code> | </code> | ||
| - | {{ :laboratoare:laborator07:ocwcoursesdisplayer.png?nolink&400 }} | + | {{ :laboratoare:laborator07:03ocwcoursesdisplayer.png?nolink&400 }} |
| ===== Prelucrare JSON (JavaScript Object Notation) - opțional ===== | ===== Prelucrare JSON (JavaScript Object Notation) - opțional ===== | ||
| Line 831: | Line 859: | ||
| * data și ora la care s-a înregistrat. | * data și ora la care s-a înregistrat. | ||
| - | {{ :laboratoare:laborator07:earthquakelister.png?nolink&400 }} | + | {{ :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 837: | Line 865: | ||
| * ''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=eim2016|]]. |
| Rezultatele sunt furnizate în următorul format: | Rezultatele sunt furnizate în următorul format: | ||
| Line 925: | Line 953: | ||
| 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. | ||
| - | {{ :laboratoare:laborator07:calculatorwebservice.png?nolink&400 }} | + | {{ :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: | ||
| Line 965: | Line 993: | ||
| 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ă). | ||
| - | {{ :laboratoare:laborator07:xkcdcartoondisplayer.png?nolink&400 }} | + | {{ :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 974: | Line 1002: | ||
| * 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 980: | Line 1008: | ||
| 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()]]; 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> | ||
| * 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 1010: | Line 1062: | ||
| <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 1019: | Line 1071: | ||
| @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 1033: | Line 1085: | ||
| <code java> | <code java> | ||
| - | new XkcdCartoonDisplayerAsyncTask().execute(Constants.XKCD_INTERNET_ADDRESS); | + | new XKCDCartoonDisplayerAsyncTask().execute(Constants.XKCD_INTERNET_ADDRESS); |
| </code> | </code> | ||
| Line 1040: | Line 1092: | ||
| 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]]. | ||
| - | {{ :laboratoare:laborator07:googlesearcher.png?nolink&400 }} | + | {{ :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 1076: | Line 1128: | ||
| * codul de țară. | * codul de țară. | ||
| - | {{ :laboratoare:laborator07:landmarklister.png?nolink&400 }} | + | {{ :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: | ||
