Image
29.6.2016 0 Comments

Vývoj pre Android – Práca s údajmi vo formátoch XML a JSON / 8. časť

Späť na úvod >> Späť na seriál

V zadaní na vytvorenie mobilnej aplikácie, ktorá sa pripája na externý zdroj údajov, dostanete adresu servera alebo webovej služby, ktorá poskytuje údaje. V praxi to znamená, že sa budete musieť prispôsobiť a aplikácia bude „konzumovať“ údaje v poskytovanom formáte, najčastejšie v platformovo nezávislých formátoch JSON a XML.

Zdroj údajov pre aplikáciu

Ako zdroj údajov vo formáte JSON aj XML využijeme populárny portál Yahoo Developer Network, konkrétne údaje o aktuálnom stave a trende akcií firiem zo služby yahoo.finance. Údaje môžete vyberať dopytom podobným SQL cez webovú konzolu https://developer.yahoo.com/yql/console/, kde treba zaškrtnúť položku Show Community Tables. Stav a trend akcií firmy Google zobrazíte pomocou dopytu

select * from yahoo.finance.quote where symbol in("GOOG")

Aby sa negenerovali aj diagnostické údaje, pole Diagnostics musí byť neoznačené.


Výber údajov

Podľa toho, či zatlačíte tlačidlo JSON alebo XML, aplikácia vygeneruje údaje v požadovanom formáte. Pre vás bude dôležité pole THE REST QUERY v dolnej časti formulára webového portálu, ktoré obsahuje kompletnú adresu URL zdroja údajov pre vašu aplikáciu. Túto adresu skopírujete do schránky a pridáte ako textový reťazec do kódu aplikácie.

Návrh používateľského rozhrania

Keďže obidve aplikácie budú využívať rovnaký zdroj údajov a líšiť sa bude iba ich formát, návrh používateľského rozhrania a definície textových reťazcov budú rovnaké pre obidve aplikácie.

Aby sme návrh čo najviac zjednodušili, budeme zobrazovať len dvojstĺpcovú tabuľku hodnôt: opis – hodnota. Na hlavnú aktivitu môžeme využiť napríklad vizuálny kontajner GridLayout. Pre jednoduchosť zobrazíme len tri údaje: zmenu hodnoty akcií, minimálnu a maximálnu dennú hodnotu.

Používateľské rozhranie hlavnej aktivity v súbore Activity_main.xml:

<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"       
    android:layout_height="match_parent"       
    android:useDefaultMargins="true"       
    android:alignmentMode="alignBounds"       
    android:columnOrderPreserved="false"       
    android:columnCount="3"
    tools:context="${packageName}.${activityClass}" >
 
   <TextView
       android:layout_gravity="right"
       android:text="@string/zmena" />
 
  <TextView
      android:layout_column="1"          
      android:id="@+id/akce"/>   
 
  <TextView
      android:layout_column="0"
      android:layout_gravity="right"
      android:text="@string/min" />
     
  <TextView
      android:layout_column="1"             
      android:id="@+id/min"/>   
     
  <TextView
      android:layout_column="0"
      android:layout_gravity="right"
      android:text="@string/max" />
     
  <TextView
     android:layout_column="1"           
     android:id="@+id/max"/> 
</GridLayout>
 
Aplikácia na demonštrovanie zobrazovania údajov získaných vo formáte JSON je jednoduchá, preto v resources budete potrebovať definovať okrem záhlavia aplikácie len tri ďalšie textové reťazce:
<resources>
    <string name="app_name">Udaje JSON</string>
    <string name="zmena">Zmena:</string>
    <string name="min">Denné minimum:</string>
    <string name="max">Denné maximum:</string>
</resources>

Aby sa vaša aplikácia mohla pripájať k údajom z internetu, musíte v aplikačnom manifeste v súbore AndroidManifest.xml deklarovať príslušné oprávnenie:

<uses-permission android:name="android.permission.INTERNET"/>

Načítanie údajov vo formáte JSON

Činnosť aplikačného kódu môžeme rozdeliť do štyroch navzájom nadväzujúcich fáz:

  • Načítanie údajov z REST API cez reťazec HTTP
  • Prevod načítaných údajov na objekt JSON
  • Parsovanie objektu JSON s cieľom zistiť hodnotu sledovaných atribútov
  • Zobrazenie načítaných údajov

Prvé tri fázy budú realizované na pozadí v asynchrónnej triede, štvrtá fáza nastúpi po úspešnom načítaní a spracovaní údajov.

Načítanie údajov z webovej služby sa bude preto realizovať v asynchrónnej triede CitajUdajeAsyncTask extends AsyncTask<String, String, String>. Po vytvorení „prázdneho“ kódu triedy pridajte cez položku kontextového menu Add unimplemented methods metódu protected String doInBackground(String... params).

V kóde metódy vytvorte objekt DefaultHttpClient a cez HttpPost otvorte prepojenie na adresu URL webovej služby. Aby ste dostali údaje vo formáte JSON, v hlavičke musíte špecifikovať reťazec:

httppost.setHeader("Content-type", "application/json");

Parser formátu JSON, žiaľ, nevie priamo načítavať z InputStreamu, preto treba obsah najskôr uložiť do textového reťazca cez BufferedReader a tento reťazec následne poslať ako parameter do konštruktora JsonObject.

jsonObject = new JSONObject(sJSON);

Údaje zo služby budú načítané prostredníctvom objektu typu InputStream cez HttpResponse. Pretože ide o textový formát, na jeho načítanie po riadkoch využijeme objekt BufferedReader a na uloženie objekt typu StringBuilder. V tejto fáze sú údaje v textovej podobe v reťazci sJSON. Vaša úloha je získať zo štruktúry hierarchického formátu JSON hodnoty atribútov, ktoré bude aplikácia vypisovať. Služba yahoo.finance vám vrátila údaje o akciách v tele funkcie

cbfunc({...});

Na parsovanie však potrebujete len telo JSON dokumentu, ktoré sa začína a končí zloženou zátvorkou. Zo začiatku dokumentu budete potrebovať odstrániť sedemznakový reťazec cbfunc( a z konca dokumentu dvojznakový reťazec );.

Ak sa pozriete na údaje JSON, zistíte, že vami požadované atribúty symbol, DaysLow a DaysHigh sú až na tretej úrovni hierarchie:

"query": {
  "results": {
   "quote": {
    "symbol": "GOOG",
    "Change": "+10.36",
    "DaysLow": "544.00",
    "DaysHigh": "553.56",
 

Musíte teda každý záznam JSON dokumentu parsovať postupne cez uzly query, results a quote. Po načítaní údajov a ukončení metódy asynchrónnej triedy môžete tieto hodnoty vložiť do prvkov typu TextView v metóde onPostExecute(String result).

Kompletný kód aplikácie v súbore MainActivity.java URL adresa je na úsporu miesta skrátená:

public class MainActivity extends Activity
{
   static String sURL = "http://query.yahooapis.com/v1/public/yql...";
 
   static String sFirma = "";
   static String sDenneMinimum = "";
   static String sDenneMaximum = "";
   static String sZmena = "";
 
   @Override
   public void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      new CitajUdajeAsyncTask().execute();
   }
 
           
           
      private class CitajUdajeAsyncTask extends AsyncTask<String, String, String>
      {
         protected String doInBackground(String... arg0)
         {
             DefaultHttpClient klientHTTP =
               new DefaultHttpClient(new BasicHttpParams());
             HttpPost httppost = new HttpPost(sURL);
            httppost.setHeader("Content-type", "application/json");
            InputStream inputStream = null;
                        
            String sJSON = null;
 
            try
            {
               HttpResponse response = klientHTTP.execute(httppost);       
               HttpEntity entity = response.getEntity();
               inputStream = entity.getContent();
               BufferedReader reader = new BufferedReader(new
                         InputStreamReader(inputStream, "UTF-8"), 8);
               StringBuilder sb = new StringBuilder();
               String riadok = null;
                                 
               while ((riadok = reader.readLine()) != null)
               {
                   sb.append(riadok + "\n");
               }
               sJSON = sb.toString();
            } catch (Exception e)
            {
             e.printStackTrace();
            }
            finally
            {
               try{if(inputStream != null)inputStream.close();}
               catch(Exception e){}
            }
 
             JSONObject jsonObject;
            try
            {
              // vymaz znaky navyse
                sJSON = sJSON.substring(7);
                sJSON = sJSON.substring(0, sJSON.length()-2);
                jsonObject = new JSONObject(sJSON);
 
                JSONObject queryJSONObject = jsonObject.getJSONObject("query");
                JSONObject resultsJSONObject =
                     queryJSONObject.getJSONObject("results");
                JSONObject quoteJSONObject =
                     resultsJSONObject.getJSONObject("quote");
                               
                sFirma = quoteJSONObject.getString("symbol");
                sDenneMinimum = quoteJSONObject.getString("DaysLow");
                sDenneMaximum = quoteJSONObject.getString("DaysHigh");
                sZmena = quoteJSONObject.getString("Change");
            } catch (JSONException e)
            {
               e.printStackTrace();
            }
            return sJSON;
         }
 
                  
   protected void onPostExecute(String result)
   {
      TextView akce = (TextView)findViewById(R.id.akce);
      TextView min = (TextView)findViewById(R.id.min);
      TextView max = (TextView)findViewById(R.id.max);
 
      akce.setText(sFirma + " : " + sZmena);
      min.setText(sDenneMinimum);
      max.setText(sDenneMaximum);
    }
}

Spustenie aplikácie

Načítanie údajov vo formáte XML

Okrem zrozumiteľnosti je hlavná výhoda dokumentov XML univerzálnosť a multiplatformovosť, preto sa veľmi často využívajú na výmenu informácií v heterogénnom prostredí. Na parsovanie údajov z dokumentu XML využijeme objekt XmlPullParser.

Kľúčová metóda je getEventType(), ktorá vracia typ objektu, ktorým práve parser prechádza. Metóda môže vrátiť nasledujúce hodnoty (fyzicky ide o celočíselnú hodnotu)

  • START_DOCUMENT – počiatočný stav parsera
  • START_TAG – počiatočný tag elementu XML
  • TEXT – indikuje, že parser je nastavený textový obsah elementu. Hodnotu textového reťazca môžete načítať pomocou metódy getText() method.
  • END_TAG – koncový tag dokumentu XML
  • END_DOCUMENT – koniec dokumentu

Pomocou metódy next() posuniete parser na ďalší objekt. Keby bolo treba načítať hodnotu atribútu, použili by ste metódu getAttributeValue():

sHodnotaAtributu = parser.getAttributeValue(null,"value");

Napriek tomu, že v tomto najjednoduchšom príklade budeme zobrazovať len údaje pre jeden objekt, v tomto prípade informácie o akciách jednej firmy, budeme postupovať systematicky a vytvoríme na zapuzdrenie načítaných údajov triedu. V kóde triedy Akcie budú len atribúty pre hodnoty, ktoré potrebujeme načítať z XML a metódy na nastavenie atribútu (setter) a získanie jeho hodnoty (getter). Na úsporu miesta sú vo výpise metódy na nastavenie atribútu (setter) a získanie jeho hodnoty (getter) iba pre jeden atribút.

public class Akcie
{
    private String symbol;
    private String change;
    private String daysLow;
    private String daysHigh;
     
    public String getSymbol()
    {
            return symbol;
    }
 
    public void setSymbol(String symbol)
    {
            this.symbol = symbol;
    }
    ...
 
 

Pri parsovaní elementov načítame najskôr textový reťazec vnútri elementu, a keď parser načíta koncový tag elementu, podľa jeho názvu priradíme textovú hodnotu príslušnej triedy. Kompletný kód aplikácie v súbore MainActivity.java URL adresa je na úsporu miesta skrátená:

public class MainActivity extends Activity
{
      static String sURL = "https://query.yahooapis.com/v1/public/yql.."; 
 
      private Akcie akcie;
      private String text;
   
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new CitajUdajeAsyncTask().execute();
    }
   
   
    public void parsujXML(XmlPullParser parser)
    {
        try
        {
            int typ = parser.getEventType();
            while (typ != XmlPullParser.END_DOCUMENT)
            {
                String sTag = parser.getName();
                switch (typ)
                {
                case XmlPullParser.START_TAG:
                    if (sTag.equalsIgnoreCase("quote"))
                    {
                        akcie = new Akcie();
                    }
                    break;
 
                case XmlPullParser.TEXT:
                    text = parser.getText();
                    break;
 
                case XmlPullParser.END_TAG:
                    if (sTag.equalsIgnoreCase("Change"))
                    {
                        akcie.setChange(text);
                    }
                    else if (sTag.equalsIgnoreCase("DaysLow"))
                    {
                         akcie.setDaysLow(text);
                    }
                    else if (sTag.equalsIgnoreCase("DaysHigh"))
                    {
                         akcie.setDaysHigh(text);
                    } else if (sTag.equalsIgnoreCase("Symbol"))
                    {
                         akcie.setSymbol(text);
                    }
                   
                    break;
 
                default: break;
                }
                typ = parser.next();
            }
        }
        catch (Exception e)
        {
           e.printStackTrace();
        }
     }
 
   
  private class CitajUdajeAsyncTask extends AsyncTask<String, String, String>
  {
      @Override
      protected String doInBackground(String... params)
      {
         Try
         {
               XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
               factory.setNamespaceAware(true);
               XmlPullParser parser = factory.newPullParser();
               parser.setInput(new InputStreamReader(getUrlData(sURL))); 
               parsujXML(parser);
         }
                  
         catch (ClientProtocolException e)
         {
            e.printStackTrace();
         } catch (XmlPullParserException e)
         {
            e.printStackTrace();
         } catch (URISyntaxException e)
         {
            e.printStackTrace();
         } catch (IOException e)
         {
            e.printStackTrace();
         } 
            finally {}
            return null;
      }
           
      public InputStream getUrlData(String url) throws URISyntaxException,
        ClientProtocolException, IOException
      {
            DefaultHttpClient client = new DefaultHttpClient();
            HttpGet method = new HttpGet(new URI(url));
            HttpResponse res = client.execute(method);
            return res.getEntity().getContent();
      }
           
           
           
      protected void onPostExecute(String result)
      {
            TextView akce = (TextView)findViewById(R.id.akce);
            TextView min = (TextView)findViewById(R.id.min);
            TextView max = (TextView)findViewById(R.id.max);
                  
            akce.setText(akcie.getSymbol() + " : " + akcie.getChange());
            min.setText(akcie.getDaysLow());
            max.setText(akcie.getDaysHigh());
      }
   }
 

V budúcom pokračovaní sa budeme venovať využitiu databázy SQLite, ktorá tvorí integrálnu súčasť operačného systému Android.

 

Zobrazit Galériu

Nechajte si posielať prehľad najdôležitejších správ emailom

Mohlo by Vás zaujímať

ITPro 1

Rozšírená realita pomôže školám

17.12.2016 00:05

Raz vidieť je lepšie ako stokrát počuť a v školstve to platí ešte mnohonásobne viac. Čo však v prípade, ak si študenti majú pozrieť, ako v reálnom čase pracuje jadrový reaktor? Alebo by chceli vidieť, ...

ITPro

Linux súkromne i pracovne v2.0 (15. časť): SIP (Session Initiation Protocol)

13.12.2016 11:58

Je priam neuveriteľné, aké množstvo užitočných informácií a faktov súvisiacich s IP telefóniou (VoIP) sa skrýva za takou jednoduchou skratkou, ako je SIP. Nejde pritom iba o protokol, ale o mnoho ďalš ...

ITPro

Výzvy a perspektívy mobilných sietí

13.12.2016 11:52

Dostupnosť kvalitného mobilného pripojenia vrátane dostatočnej kapacity na prenos dát považujeme v súčasnosti za samozrejmosť.  O niektorých špecifikách a  perspektívach služieb mobilných operátorov v ...

Žiadne komentáre

Vyhľadávanie

qubitconference

Najnovšie videá