programmazione mobile: android
TRANSCRIPT
ITIS Max Planck di Lancenigo di Villorba (TV) A.S. 2013-2014
Prof. PAOLO TOSATO
Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Programmazione mobile: ANDROID Quarta lezione: file system, parsing file XML e networking
09/07/2016 2
• Accesso al file system
• Storage interno • Storage esterno
• Parse XML data • XML • Parser XML
• Networking
Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Indice
09/07/2016 3 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Accesso al file system
Storage interno
• Porzione di file system esclusivamente assegnata all’applicazione: altri pacchetti installati nel dispositivo non possono farvi accesso.
Storage esterno
• Solitamente una scheda che può all’occorrenza essere rimossa e sostituita, ma non è detto.
Accesso al file system
09/07/2016 4 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Storage interno Per scoprire quale sia la directory radice dello spazio riservato ad un’applicazione: getFilesDir(), che restituisce un java.io.File. Metodo di android.content.Context, classe da cui derivano Activity e gli altri mattoni fondamentali di Android. protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mylayout); /* Dov'è lo storage interno dell'applicazione? (In Eclipse: DDMS File Explorer) /data/data/<package applicazione>/files */ Log.i("FileDemo", "Directory: " + getFilesDir().getAbsolutePath()); }
09/07/2016 5 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Accesso al file system
Storage esterno • Prima cosa da fare: controllare se disponibile! public static String getExternalStorageState(), metodo di android.os.Environment
• Environment.MEDIA_MOUNTED disponibile in lettura e scrittura • Environment.MEDIA_MOUNTED_READ_ONLY disponibile solo in lettura • Environment.XXX …
• Per ottenere la radice dello storage esterno public static File getExternalStorageDirectory(), metodo di android.os.Environment
• Si sconsiglia di creare file direttamente nella radice dello storage. Android infatti organizza il suo storage esterno con una serie di directory standard (Music per la musica, Download per i file scaricati, ecc.).
09/07/2016 6 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
XML Oggigiorno difficilmente un’applicazione lavora in modo isolato e possiede tutti i dati che deve elaborare. Problema: raramente si deve interagire con applicazioni che possiedono la stessa tecnologia. Soluzione: scambiare informazioni attraverso un formato aperto come Extensible Markup language (XML)
09/07/2016 7 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
XML
Metalinguaggio per la rappresentazione dei dati a struttura gerarchica in forma di testo. Consente di definire in modo semplice nuovi linguaggi di markup da usare in ambito web.
<?xml version="1.0" encoding="UTF-8"?> <products> <product> <productname>Pantaloni</productname> <productcolor>nero</productcolor> <productquantity>5</productquantity> </product> <product> <productname>T-Shirt</productname> <productcolor>blu</productcolor> <productquantity>3</productquantity> </product> </products>
09/07/2016 8 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
XML
Un documento XML è: • un documento di testo • “human readable” • contiene tag (di apertura e chiusura) che possono avere attributi e dati al loro interno • case sensitive • deve essere ben formattato, cioè avere:
• un prologo: <?xml version="1.0" encoding="UTF-8"?> • un unico elemento radice: <products> • tutti i tag devono essere bilanciati (corretto annidamento)
Se il documento XML non contiene errori si dice Well Formed (ben formato). Se il documento è well formed e in più rispetta i requisiti strutturali definiti nel file DTD (Document Type Definition ) o schema XML associato viene chiamato Valid (valido)
09/07/2016 9 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
Parser XML
DOM (Document Object Model): è un'interfaccia di programmazione per la manipolazione di file XML. DOM, partendo dal file XML, costruisce un albero dove ogni nodo dell'albero corrisponde ad un elemento del file; per questo motivo è detto tree based o object based
• Consuma molta memoria
• Permette di fare il parse e creare un file XML
• Effettuare il parse di tutto il documento
09/07/2016 10 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
Parser XML
SAX (Simple API for XML): è un'interfaccia di programmazione che permette di leggere e modificare i documenti XML. SAX è event based, al contrario di DOM, e reagisce agli eventi di parsing facendo rapporto all'applicazione. È compito del programmatore implementare i metodi per reagire agli eventi di parsing ().
• Consuma molta meno memoria del DOM (ottimo per grandi documenti) • Non può creare file XML, ma solo effettuare il parse • Effettuare il parse di tutto il documento
PULL: simile a SAX, ma più semplice da implementare e più performante.
• Si può effettuare il parse solo di un nodo specifico (“pull only a particular node”) • Consigliato per piccoli documenti.
09/07/2016 11 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Parse XML data
XmlPullParser: esempio try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xpp = factory.newPullParser(); xpp.setInput(new StringReader( "<saluto>Hello World!</saluto>" )); /* * Restituisce lo stato corrente del parser * Inizialmente 0 (START_DOCUMENT) * Alla fine 1 (END_DOCUMENT) */ int eventType = xpp.getEventType();
Parse XML data
09/07/2016 12 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
XmlPullParser: esempio while (eventType != XmlPullParser.END_DOCUMENT) { if(eventType == XmlPullParser.START_DOCUMENT) { Log.i("MYAPP", "Start document"); } else if(eventType == XmlPullParser.START_TAG) { // 2 Log.i("MYAPP", "Start tag: " + xpp.getName()); } else if(eventType == XmlPullParser.END_TAG) { // 3 Log.i("MYAPP", "End tag: " + xpp.getName()); } else if(eventType == XmlPullParser.TEXT) { // 4 Log.i("MYAPP", "Testo: " + xpp.getText()); } eventType = xpp.next(); } Log.i("MYAPP", "End document"); } catch(XmlPullParserException | IOException e) { Log.e("MYAPP", "Parser Exception"); }
Parse XML data
09/07/2016 13 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
XmlPullParser: esempio XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser xpp = factory.newPullParser(); Factory Design pattern
• Disaccoppiare le modalità di creazione degli oggetti dal loro utilizzo.
• Factory incapsula la logica di costruzione (connessioni EJB, DB, ecc): cambiamenti nella costruzione della classe comportano modifiche alla sola classe Factory.
• La creazione di un oggetto richiede l'accesso ad informazioni o risorse che non dovrebbero essere contenute nella classe di composizione.
• La gestione del ciclo di vita degli oggetti deve essere centralizzata in modo da assicurare un comportamento consistente all'interno dell'applicazione.
Parse XML data
09/07/2016 14 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
XmlPullParser
• E se volessimo accedere a una risorsa presente nel file system?
/* * getAssets() restituisce un AssetManager, che permette di accedere * alle risorse della directory assets (non hanno un riferimento in R) */ InputStream file = getApplicationContext().getAssets().open("data.xml"); xpp.setInput(file, null);
Parse XML data
09/07/2016 15 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
XmlPullParser
• E se volessimo memorizzare le informazione in un oggetto ad hoc? ArrayList<Prodotto> prodotti = null; Prodotto currentProduct = null; int eventType = xpp.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String name = null; switch (eventType) { case XmlPullParser.START_DOCUMENT: prodotti = new ArrayList<Prodotto>(); break;
Parse XML data
09/07/2016 16 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
case XmlPullParser.START_TAG: name = xpp.getName(); if (name.equals("product")) { currentProduct = new Prodotto(); } else if (currentProduct != null) { if (name.equals("productname")) { currentProduct.setNome(xpp.nextText()); } else if (name.equals("productcolor")) { currentProduct.setColore(xpp.nextText()); } else if (name.equals("productquantity")) { currentProduct.setQuantita(Integer.parseInt(xpp.nextText())); } } break;
Parse XML data
09/07/2016 17 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
case XmlPullParser.END_TAG: name = xpp.getName(); if (name.equals("product") && currentProduct != null){ prodotti.add(currentProduct); } } // chiude lo switch eventType = xpp.next(); } // chiude il ciclo while
Parse XML data
09/07/2016 18 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
/* * Stampa l’array list su LogCat */ StringBuffer content = new StringBuffer(); Iterator<Prodotto> iterator = prodotti.iterator(); while(iterator.hasNext()) { Prodotto prodotto = iterator.next(); content.append("Nome: " + prodotto.getNome() + " "); content.append("Colore: " + prodotto.getColore() + " "); content.append("Quantita': " + prodotto.getQuantita()); Log.i("MYAPP", content.toString()); content.delete(0, content.length()); }
Net
09/07/2016 19 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
AsyncTask
• Questa classe consente di eseguire operazioni in background e di pubblicarne in modo semplice i risultati nel Thread dell'interfaccia grafica (UI Thread).
• Dovrebbe essere utilizzato per operazioni di breve durata (pochi secondi al massimo).
• E' definito da 3 tipi generici: Params, Progress e Result e da 4 fasi: OnPreExecute, doInBackground, onProgressUpdate e OnPostExecute.
• Per essere implementato è necessario estendere la classe AsyncTask e sovrascrivere il metodo doInBackground(Params...)
09/07/2016 20 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Net
AsyncTask: tipi generici • Params, il tipo dei parametri inviati al task al momento dell'esecuzione.
• Progress, il tipo delle progress unit pubblicate durante le operazioni in background.
• Result, il tipo di risultato dell'elaborazione in background.
Non tutti i tipi devono essere utilizzati da un task asincrono. Per indicare che un tipo non è utilizzato si utilizza Void: private class MyTask extends AsyncTask<Void, Void, Void> { ... }
Net
09/07/2016 21 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
AsyncTask: 4 fasi
• onPreExecute(), invocato sul thread dell'interfaccia grafica prima che il task venga eseguito. Questa fase viene normalmente utilizzata per il setup del task, per esempio per mostrare una progress bar nell'interfaccia utente.
• doInBackground(Params...), invocato quando termina onPreExcecute() e serve per avviare il task asincrono. Il risultato dell'elaborazione deve essere restituito a questo metodo. Questa fase può utilizzare publishProgress(Progress...) per pubblicare delle unità di progressione. Questi valori sono pubblicati nell'interfaccia utente tramite onProgressUpdate(Progress...).
• onProgressUpdate(Progress...), invocato da publishProgress(Progress...). Questo metodo è utilizzato per visualizzare qualsiasi forma di progresso nell'interfaccia utente mentre il task è in esecuzione in background. Per esempio, può essere utilizzato per animare una progress bar.
• onPostExecute(Result), invocato dopo che l'elaborazione in backgroud è terminata. Il risultato di questa elaborazione è passato al metodo come parametro.
Net
09/07/2016 22 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Permessi
Settare l’app in modo che possa connettersi ad Internet AndroidManifest Permission ADD Uses Permission android.permission.INTERNET
<uses-permission android:name="android.permission.INTERNET"/>
<application <activity android:name="esempi.android.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
Net
09/07/2016 23 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
private static final String TAG = "MYAPP"; // Parametri della richiesta HTTP private static final String VERB = "ListRecords"; private static final String METADATA_PREFIX = "oai_lom"; // QUERY URL private static final String SERVER_URL = "http://cird.unive.it/"; private static final String QUERY_FILE = "oai/request"; private static final String QUERY_OPTIONS = "?verb=" + VERB + "&metadataPrefix=" + METADATA_PREFIX; private static final String QUERY_URL = SERVER_URL + QUERY_FILE + QUERY_OPTIONS;
09/07/2016 24 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
Net
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btDownload = (Button) findViewById(R.id.btConnect); btDownload.setOnClickListener(this); } public void onClick(View v) { TextView txtNumRec = (TextView) findViewById(R.id.txtNumRec); txtNumRec.setText("0"); AsyncDownload downloader = new AsyncDownload(); // Chiamo il Thread downloader.execute(); } public void printResult(int numRecord) { TextView txtNumRec = (TextView) findViewById(R.id.txtNumRec); txtNumRec.setText(String.valueOf(numRecord)); }
Net
09/07/2016 25 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
private class AsyncDownload extends AsyncTask<Object, String, Integer> { // Inner class per fare il download in background protected Integer doInBackground(Object... params) { XmlPullParser datiRicevuti = downloadXmlData(); int numRecords = parsingXmlData(datiRicevuti); return numRecords; } private XmlPullParser downloadXmlData() { Log.i(TAG, "Download avviato ..."); try { URL xmlUrl = new URL(QUERY_URL); XmlPullParser datiRicevuti = XmlPullParserFactory.newInstance().newPullParser(); datiRicevuti.setInput(xmlUrl.openStream(), null); return datiRicevuti; } catch (MalformedURLException e) { Log.e(TAG, "URL errato", e); } catch (XmlPullParserException e) { Log.e(TAG, "Factory error", e); } catch (IOException e) { Log.e(TAG, "Errore di connessione", e); } return null; }
Net
09/07/2016 26 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
private int parsingXmlData(XmlPullParser datiRicevuti) { int numRecord = 0; if (datiRicevuti != null) { try { int eventType = datiRicevuti.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String nome = null; switch(eventType) { case XmlPullParser.START_TAG: nome = datiRicevuti.getName(); if (nome.equals("record")) { numRecord++; publishProgress(String.valueOf(numRecord)); // Chiama onProgressUpdate } break; } eventType = datiRicevuti.next(); } } catch (XmlPullParserException e) { Log.e(TAG, "PullParser error", e); } catch (IOException e) { Log.e(TAG, "PullParser error", e); } } return numRecord; }
Net
09/07/2016 27 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); if (values.length == 1) { printResult(Integer.parseInt(values[0])); } } } }
09/07/2016 28 Quest' opera è distribuita con licenza Creative Commons Attribuzione - Non commerciale - Condividi allo stesso modo 3.0 Unported.
The end!