Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
colocvii:colocviu02 [2015/05/16 19:44]
Andrei Roșu-Cojocaru
colocvii:colocviu02 [2015/05/16 21:28] (current)
Andrei Roșu-Cojocaru
Line 2: Line 2:
  
 ===== Model de Subiect ===== ===== Model de Subiect =====
 +
 +<​HTML>​
 +<​center>​
 +</​HTML>​
 +
 +{{url>​http://​pdsd2015.andreirosucojocaru.ro/​_media/​colocvii/​colocviu02/​pdsd2015-tp02-varsim.pdf 90%,800px}}
 +
 +<​HTML>​
 +</​center>​
 +</​HTML>​
 +
 +{{ colocvii:​colocviu02:​pdsd2015-tp02-varsim.pdf }}
  
 ===== Observații Generale ===== ===== Observații Generale =====
Line 38: Line 50:
 Se creează o zonă de lucru corespunzătoare unui proiect prin intermediului butonului //New Repository//​. Se creează o zonă de lucru corespunzătoare unui proiect prin intermediului butonului //New Repository//​.
  
-{{ :colocvii:colocviu01:​github_new_repository.png?​nolink }}+{{ :colocvii:colocviu02:​github_new_repository.png?​nolink }}
  
 Configurarea depozitului la distanță presupune specificarea:​ Configurarea depozitului la distanță presupune specificarea:​
Line 51: Line 63:
   * tipului de licență sub care este publicat codul sursă.   * tipului de licență sub care este publicat codul sursă.
  
-{{ :colocvii:colocviu01:​github_repository_configuration.png?​nolink }}+{{ :colocvii:colocviu02:​github_repository_configuration.png?​nolink }}
  
 **2.** Prin intermediul comenzii ''​git clone''​ se poate descărca întregul conținut în directorul curent (de pe discul local), inclusiv istoricul complet al versiunilor anterioare (care poate fi ulterior reconstituit după această copie, în cazul coruperii informațiilor stocate pe serverul la distanță). **2.** Prin intermediul comenzii ''​git clone''​ se poate descărca întregul conținut în directorul curent (de pe discul local), inclusiv istoricul complet al versiunilor anterioare (care poate fi ulterior reconstituit după această copie, în cazul coruperii informațiilor stocate pe serverul la distanță).
Line 293: Line 305:
  
 Pornirea firului de execuție corespunzător serverului va fi realizată pe metoda de callback a obiectului ascultător pentru evenimentul de tip apăsare a butonului aferent din interfața grafică: Pornirea firului de execuție corespunzător serverului va fi realizată pe metoda de callback a obiectului ascultător pentru evenimentul de tip apăsare a butonului aferent din interfața grafică:
 +
 +În prealabil, trebuie să se verifice completarea câmpului text care conține portul pe care vor fi acceptate conexiuni de la clienți.
  
 <code java> <code java>
Line 350: Line 364:
  
 **b)** un fir de execuție care gestionează comunicația dintre client și server: **b)** un fir de execuție care gestionează comunicația dintre client și server:
- 
   * se obțin obiectele de tip ''​BufferedReader''​ și ''​PrintWriter''​ (prin apelul metodelor statice din clasa ''​Utilities'':​ ''​getReader()''​ și ''​getWriter()''​),​ prin care se vor realiza operațiile de citire și scriere pe canalul de comunicație;​   * se obțin obiectele de tip ''​BufferedReader''​ și ''​PrintWriter''​ (prin apelul metodelor statice din clasa ''​Utilities'':​ ''​getReader()''​ și ''​getWriter()''​),​ prin care se vor realiza operațiile de citire și scriere pe canalul de comunicație;​
   * se obține o referință către obiectul gestionat de server ce reține informațiile meteorologice pentru orașele pentru care au fost realizate interogări anterior;   * se obține o referință către obiectul gestionat de server ce reține informațiile meteorologice pentru orașele pentru care au fost realizate interogări anterior;
Line 356: Line 369:
   * în situația în care informațiile meteorologice se găsesc în obiectul gestionat de server, sunt preluate local;   * în situația în care informațiile meteorologice se găsesc în obiectul gestionat de server, sunt preluate local;
   * în situația în care informațiile meteorologice nu se găsesc în obiectul gestionat de server, sunt preluate prin interogarea serviciului Internet, la distanță:   * în situația în care informațiile meteorologice nu se găsesc în obiectul gestionat de server, sunt preluate prin interogarea serviciului Internet, la distanță:
-    * se realizează o cerere de tip ''​POST''​ la adresa Internet [[http://​www.wunderground.com/​cgi-bin/​findweather/​getForecast]];​ +    * se realizează o cerere de tip ''​POST''​ la adresa Internet [[http://​www.wunderground.com/​cgi-bin/​findweather/​getForecast]];​ parametrul care trebuie precizat este ''​query''​ și are valoarea orașului pentru care se dorește să se obțină informațiile;​ acesta se atașează cererii de tip ''​POST'',​ folosind codificarea ​''​UTF-8'';​ 
-    * parametrul care trebuie precizat este ''​query''​ și are valoarea orașului pentru care se dorește să se obțină informațiile;​ acesta se atașează cererii de tip ''​POST'',​ folosind codificarea UTF-8 (alternativ, se poate realiza o cerere de tip ''​GET''​ în care valoarea parametrului este inclusă în cadrul adresei Internet: [[http://​www.wunderground.com/​cgi-bin/​findweather/​getForecast?​query=city]]);+    * alternativ, se poate realiza o cerere de tip ''​GET''​ în care valoarea parametrului este inclusă în cadrul adresei Internet: [[http://​www.wunderground.com/​cgi-bin/​findweather/​getForecast?​query=...]]);
     * se obține răspunsul sub forma unui șir de caractere, reprezentând codul sursă al paginii Internet; ​     * se obține răspunsul sub forma unui șir de caractere, reprezentând codul sursă al paginii Internet; ​
     * folosind biblioteca Jsoup, se inspectează documentul în format HTML furnizat pentru a determina locația la care sunt disponibile informațiile necesare <code html>     * folosind biblioteca Jsoup, se inspectează documentul în format HTML furnizat pentru a determina locația la care sunt disponibile informațiile necesare <code html>
Line 533: Line 546:
 </​code>​ </​code>​
  
-**6.**+**6.** ​Implementarea clientului presupune un fir de execuție pe care sunt realizate următoarele operații:​ 
 +  * deschiderea unui canal de comunicație folosind parametrii de conexiune la server (adresa Internet, port); 
 +  * obținerea unor obiecte de tip ''​BufferedReader''​ și ''​PrintWriter''​ (prin apelul metodelor statice din clasa ''​Utilities'':​ ''​getReader()''​ și ''​getWriter()''​),​ prin care se vor realiza operațiile de citire și scriere pe canalul de comunicație;​ 
 +  * trimiterea la server (prin scriere pe canalul de comunicație) parametrii necesari pentru furnizarea informațiilor meteorologice (oraș și tip de date: temperatură,​ viteza vântului, starea generală, presiune, umiditate, toate); 
 +  * primirea de la server (prin citire de pa canalul de comunicație) a liniilor distincte conținând datele meteorologice (cât timp nu se primește EOF, reprezentat printr-o valoare ''​null''​ a șirului de caractere furnizat);​ 
 +  * vizualizarea datelor meterologice într-un obiect de tip ''​TextView'',​ din cadrul interfeței grafice; întucât modificarea se face din contextul unui fir de execuție care gestionează comunicația prin rețea, accesul la controlul grafic se face prin intermediul metodei ''​post()''​ care primește ca parametru un obiect anonim de tip ''​Runnable''​ al cărui conținut va fi rulat în contextul firului de execuție principal al aplicației Android; 
 +  * închiderea canalului de comunicație. 
 + 
 +<code java> 
 +@Override 
 +public void run() { 
 +  try { 
 +    socket = new Socket(address,​ port); 
 +    if (socket == null) { 
 +      Log.e(Constants.TAG,​ "​[CLIENT THREAD] Could not create socket!"​);​ 
 +      return; 
 +    } 
 +    BufferedReader bufferedReader = Utilities.getReader(socket);​ 
 +    PrintWriter ​   printWriter ​   = Utilities.getWriter(socket);​ 
 +    if (bufferedReader != null && printWriter != null) { 
 +      printWriter.println(city);​ 
 +      printWriter.flush();​ 
 +      printWriter.println(informationType);​ 
 +      printWriter.flush();​ 
 +      String weatherInformation;​ 
 +      while ((weatherInformation = bufferedReader.readLine()) != null) { 
 +        final String finalizedWeatherInformation = weatherInformation;​ 
 +        weatherForecastTextView.post(new Runnable() { 
 +          @Override 
 +          public void run() { 
 +            weatherForecastTextView.append(finalizedWeatherInformation + "​\n"​);​ 
 +          } 
 +        }); 
 +      } 
 +    } else { 
 +      Log.e(Constants.TAG,​ "​[CLIENT THREAD] BufferedReader / PrintWriter are null!"​);​ 
 +    } 
 +    socket.close();​ 
 +  } catch (IOException ioException) { 
 +    Log.e(Constants.TAG,​ "​[CLIENT THREAD] An exception has occurred: " + ioException.getMessage());​ 
 +    if (Constants.DEBUG) { 
 +      ioException.printStackTrace();​ 
 +    } 
 +  } 
 +} 
 +</​code>​ 
 + 
 +{{ :​colocvii:​colocviu02:​graphic_interface02.png?​nolink&​400 }} 
 + 
 +Pornirea firului de execuție corespunzător clientului va fi realizată pe metoda de callback a obiectului ascultător pentru evenimentul de tip apăsare a butonului aferent din interfața grafică. 
 + 
 +Verificările care trebuie realizate sunt: 
 +  * completarea câmpurilor text ce conțin parametrii de conexiune la server (adresă Internet, port); 
 +  * existența unui fir de execuție corespunzător serverului care să ruleze la momentul respectiv;​ 
 +  * completarea controalelor grafice ce conțin denumirea orașului și tipul de informație meteorologică ce se dorește a fi solicitată. 
 + 
 +<code java> 
 +private class GetWeatherForecastButtonClickListener implements Button.OnClickListener { 
 +  @Override 
 +  public void onClick(View view) { 
 +    String clientAddress = clientAddressEditText.getText().toString();​ 
 +    String clientPort ​   = clientPortEditText.getText().toString();​ 
 +    if (clientAddress == null || clientAddress.isEmpty() || 
 +      clientPort == null || clientPort.isEmpty()) { 
 +      Toast.makeText( 
 +        getApplicationContext(),​ 
 +        "​Client connection parameters should be filled!",​ 
 +        Toast.LENGTH_SHORT 
 +      ).show(); 
 +      return; 
 +    } 
 +    if (serverThread == null || !serverThread.isAlive()) { 
 +      Log.e(Constants.TAG,​ "[MAIN ACTIVITY] There is no server to connect to!"​);​ 
 +      return; 
 +    } 
 +    String city = cityEditText.getText().toString();​ 
 +    String informationType = informationTypeSpinner.getSelectedItem().toString();​ 
 +    if (city == null || city.isEmpty() || 
 +      informationType == null || informationType.isEmpty()) { 
 +      Toast.makeText( 
 +        getApplicationContext(),​ 
 +        "​Parameters from client (city / information type) should be filled!",​ 
 +        Toast.LENGTH_SHORT 
 +      ).show(); 
 +      return; 
 +    } 
 +    weatherForecastTextView.setText(Constants.EMPTY_STRING);​ 
 +    clientThread = new ClientThread( 
 +      clientAddress,​ 
 +      Integer.parseInt(clientPort),​ 
 +      city, 
 +      informationType,​ 
 +      weatherForecastTextView);​ 
 +    clientThread.start();​ 
 +  } 
 +} 
 +</​code>​ 
 + 
 +Conexiunea la serverul implementat în cadrul aplicației Android se poate realiza și prin intermediul unei console de pe mașina fizică, folosind utilitarele ''​nc''​ (Linux), respectiv ''​telnet''​ (Windows). 
 + 
 +Trebuie determinată adresa Internet la care poate fi accesat dispozitivul mobil: 
 +  * dispozitiv fizic: se folosește USB Tethering, iar adresa Internet este indicată de valoarea //Default Gateway// a rețelei ''​usb0''​ / ''​rndis0''​ - Linux, respectiv Ethernet pe Windows (se rulează comenzile ''​ifconfig''​ pe Linux, ''​ipconfig''​ pe Windows); 
 +  * dispozitiv virtual: 
 +    * Genymotion: adresele Internet sunt alocate de serverul DHCP din cadrul VirtualBox în intervalul ''​192.168.56.101''​ → ''​192.168.56.254'';​ 
 +    * AVD: se utilizează redirectarea porturilor. 
 + 
 +<columns 100 50> 
 + 
 +<​code>​ 
 +student@pdsd2015:​~$ nc 192.168.56.101 5000 
 +Bucuresti 
 +all 
 +temperature:​ 17.0 
 +wind_speed: 5.0 
 +condition: Clear 
 +pressure: 1015 
 +humidity: 68 
 +</​code>​ 
 + 
 +<​newcolumn>​ 
 + 
 +<​code>​ 
 +C:​\Users\Pdsd2015>​ telnet 192.168.56.101 5000 
 +Bucuresti 
 +all 
 +temperature:​ 17.0 
 +wind_speed: 5.0 
 +condition: Clear 
 +pressure: 1015 
 +humidity: 68 
 +Connection to host lost. 
 +</​code>​ 
 + 
 +</​columns>​
  
 **7.** Pentru încărcarea codului în contextul depozitului din cadrul contului Github personal: **7.** Pentru încărcarea codului în contextul depozitului din cadrul contului Github personal:
Line 555: Line 701:
 </​code>​ </​code>​
  
-**8.**+**8.** ​În situația în care un server de aplicații dorește să transmită un mesaj către un dispozitiv mobil prin intermediul serviciului Google Cloud Messaging, specificând în antetul ''​Authorization''​ al cererii o cheie publică ce nu este asociată numărului de proiect pe baza căruia a fost generat identificatorul de înregistrare transmis ca valoare a parametrului ''​registration_id'',​ respectiv ''​registration_ids'',​ răspunsul va fi ''​MismatchSenderId''​. Acesta indică faptul că utilizatorul respectiv nu are dreptul de a transmite notificări către dispozitivul mobil. În aceste condiții, notificarea nu va fi transmisă, acest lucru fiind precizat prin contorizarea sa în secțiunea ''​failure''​.
  
-**9.**+**9.** ​Înregistrarea unui serviciu în cadrul rețelei locale se face prin intermediul unor obiecte ''​NsdServiceInfo''​ (Android NSD) respectiv ''​ServiceInfo''​ (JmDNS). Acestea sunt transmise ca parametrii ai metodelor ''​registerService()''​ din clasele ''​NsdManager'',​ respectiv ''​JmDNS''​.
  
-**10.**+Aceste obiecte sunt caracterizate prin parametrii de conectare la serviciu: 
 +  ​* ''​NsdServiceInfo''​ - adresaInternet (metoda ''​setHost()''​) și port (metoda ''​setPort()''​);​ 
 +  ​* ''​ServiceInfo''​ - portul, configurat prin intermediul constructorului.
  
 +Se recomandă ca portul să fie furnizat de sistemul de operare, pentru a evita situațiile în care utilizatorul poate specifica un port care este ocupat. În acest sens, se instanțiază un obiect de tip ''​ServerSocket''​ care primește parametrul ''​0'',​ indicându-se astfel faptul că se dorește utilizarea unui port aleator, care poate fi folosit la momentul respectiv. Ulterior, parametrii de conectare pot fi obținuți folosind metodele specifice ale unui astfel de obiect:
 +  * adresa Internet - ''​getInetAddress()'';​
 +  * port - ''​getLocalPort()''​.
 +
 +<code java>
 +ServerSocket serverSocket = new ServerSocket(0);​
 +if (serverSocket != null) {
 +  InetAddress inetAddress = serverSocket.getInetAddress();​
 +  int port = serverSocket.getLocalPort();​
 +}
 +</​code>​
 +
 +**10.** Harta Google este implementată în SDK-ul Android:
 +  * prin intermediul unei componente grafice de tipul [[https://​developer.android.com/​reference/​com/​google/​android/​gms/​maps/​MapView.html|MapView]];​
 +  * în cadrul unui fragment, de tipul [[http://​developer.android.com/​reference/​com/​google/​android/​gms/​maps/​MapFragment.html|MapFragment]].
 +
 +Referințele către aceste obiecte se obțin în mod obișnuit, prin intermediul metodelor ''​findViewById()'',​ respectiv ''​findFragmentById()''​.
 +
 +Pe baza controalelor grafice ''​MapView''​ sau ''​MapFragment'',​ se poate obține o instanță a unui obiect [[https://​developer.android.com/​reference/​com/​google/​android/​gms/​maps/​GoogleMap.html|GoogleMap]],​ prin intermediul metodelor ''​getMap()'',​ respectiv ''​getMapAsync()''​. Se recomandă să se folosească metoda asincronă care garantează faptul că obiectul furnizat este nenul. Metoda de callback ''​onMapReady()''​ a clasei ascultător ''​OnMapReadyCallback''​ nu va fi apelată în situația în care serviciul Google Play Services nu este disponibil pe dispozitivul mobil sau obiectul este distrus imediat după ce a fost creat.
 +
 +<code java>
 +if (googleMap == null) {
 +  if (googleMap == null) {
 +    ((MapFragment)getFragmentManager().findFragmentById(R.id.google_map)).getMapAsync(new OnMapReadyCallback() {
 +      @Override
 +      public void onMapReady(GoogleMap readyGoogleMap) {
 +        googleMap = readyGoogleMap;​
 +      }
 +    });
 +  }
 +}
 +</​code>​
colocvii/colocviu02.1431794673.txt.gz · Last modified: 2015/05/16 19:44 by Andrei Roșu-Cojocaru
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0