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

Právne okienko

08.12.2016 12:02

1. Ako postupovať, ak obchodník nechce uznať reklamáciu tovaru objednaného z e-shopu? V takomto prípade môžu nastať v zásade dve situácie. Ak zákazník reklamuje tovar do 12 mesiacov od jeho kúpy, mož ...

ITPro

Linux súkromne i pracovne v2.0 (14. časť): Small Business Server

09.11.2016 14:57

Pojem Small Business Server (malý firemný server) začala používať spoločnosť Microsoft ešte v roku 2000 na označenie servera, ktorý ­dokázal plniť úlohy niekoľkých samostatných serverov. Aplikačná vrs ...

ITPro

Industry 4.0: Fikcia alebo už realita?

09.11.2016 14:52

Štvrtá priemyselná revolúcia je pomenovanie rozsiahlych zmien prudko vstupujúcich do súčasného priemyslu. Nositeľom týchto zmien je digitalizácia výroby a optimalizácia všetkých podnikových procesov v ...

Žiadne komentáre

Vyhľadávanie

Kyocera - prve-zariadenia-formatu-a4-s-vykonom-a3

Najnovšie videá