
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