Main Menu

Disclaimer

Creative Commons License

I contenuti di questo sito sono liberamente riproducibili con obbligo di citare la fonte BeerBaron Forum - Homebrewing and More e con espresso divieto di cederli a terzi a qualsiasi titolo nonche di utilizzarli a fini commerciali, promo-pubblicitari o di lucro.

Questo sito non rappresenta una testata giornalistica in quanto viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62 del 7.03.2001

Privacy e Cookie Policy

avatar_BeerBaron

Storia di un'app

Started by BeerBaron, May 17, 2013, 03:34:23 PM

0 Members and 1 Guest are viewing this topic.

BeerBaron

Siccome questa settimana ho veramente poco un cazzo da fare, tra dimissioni imminenti e viaggi da disoccupato, apro questo spazio come un semplice blog di sviluppo di una app Android.

L'idea di base è di tracciare le difficoltà (e si spera le soluzioni) incontrate durante lo sviluppo, e di raccogliere indicazioni sulle funzionalità che potrebbero essere inserite, aggiornate o modificate.

L'app che ho in mente è un piccolo client compatibile con Tapatalk.
Questo significa che deve essere almeno in grado di:
- comunicare con la rete
- gestire l'autenticazione
- gestire più di un forum

Per la comunicazione di rete, in Android, è sufficiente abilitare la Permission INTERNET nel file AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>

Il framework di Tapatalk prevede un protocollo di comunicazione XML-RPC, in cui si passano fondamentalmente il metodo da chiamare e gli eventuali parametri in un file xml, e si attende la risposta che arriverà allo stesso modo in un file xml dalla struttura abbastanza regolare.

Per prima cosa mi sono messo a studiare il protocollo XML-RPC.
Il risultato finale dei miei studi mi porta a dire che si tratta di un protocollo estremamente versatile, tuttavia poco e malamente documentato.
Il mio primo tentativo è stato generare una semplice classe in grado di inviare e ricevere una XML-RPC request, quindi un HttpClient normalissimo, e un Header specifico xml:


    HttpPost httppost = new HttpPost(webserviceUrl);         
    StringEntity se = null;
    try {
        se = new StringEntity(request);
    } catch (UnsupportedEncodingException e) { }

    se.setContentType("text/xml"); 
    httppost.setHeader("Content-Type", "application/soap+xml;charset=UTF-8");
    httppost.setEntity(se); 
    HttpClient httpclient = new DefaultHttpClient();
    String out = "";
    try {
        BasicHttpResponse httpResponse = (BasicHttpResponse) httpclient.execute(httppost);
        BasicManagedEntity bma = (BasicManagedEntity) httpResponse.getEntity();
        InputStream is = bma.getContent();
        BufferedReader r = new BufferedReader(new InputStreamReader(is, "US-ASCII"));

        String line;
        while ((line = r.readLine()) != null) {
            out += line;
        }

    } catch (ClientProtocolException e) {
        return null;
    } catch (IOException e) {
        return null;
    }
    return out;


Questa semplice implementazione mi ha permesso di testare la più semplice delle chiamate al plugin, ovvero la "get_config". Il risultato è una stringa (out nell'esempio) contente l' XML fitto fitto di coppie chiave-valore che riassumono la configurazione del forum.

:looksi:
medel è il nuovo burdisso

Ferro

Avre bene le orecchie...ascolta pendejo.
Prova a fare una delle tue estronsate de pazzo, prova a tirare fuori el ferro...
Io te lo estrapo de mano, te lo metto en el culo e poi premo el grilletto hasta che siento el clic!
"Jesus"




BeerBaron

Quote from: Ferro on May 17, 2013, 03:43:41 PM
Io nel tempo libero mi faccio delle seghe.

anche io, infatti ste cose le sto facendo al lavoro  :ph34r:
medel è il nuovo burdisso

d€$mo

@barone...XML-RPC...puoi evitare di usare sigle per non bestemmiare :patpat:
aap
majmun
Aaya
majom
unggoy
Affe
biri
tumbili
tus liab
enwe
daanyeer

Porco Dio

BeerBaron

A questo punto, osservando la struttura del file ottenuto, mi sono detto che forse era il caso di creare un parser dedicato che si smazzasse 'sto output e si salvasse le informazioni da qualche parte.
Essendo un xml non mi sono messo a reinventare l'acqua calda, ma ho utilizzato le librerie DOM fornite da Apache.

NodeList nodes = doc.getElementsByTagName("member");

Ho litigato un po' con la navigazione della struttura del file, però poi mi sono districato e sono riuscito ad ottenere l'accesso alle "foglie" dell'xml e al loro valore, wrappando la ricerca con opportune funzioni parametriche:


public ResponseMember getMemberByName (String name) {
    if(rm != null) {
        // iterate the members
        for (int i = 0; i < rm.length; i++) {
            if(rm[i].getName().equals(name)) {
                return rm[i];
            }
        }
    }
    return null;
}


Ho dovuto creare una struttura d'appoggio ResponseMember perchè ho visto che sto maledetto plugin invia le informazioni in coppie nome-valore in cui il valore è una sottostruttura che ne specifica il tipo. Per cui potrebbe essere una stringa, un intero, un booleano, un base64 e così via. Quindi mi sono dovuto anche fare un sistemino in grado di determinare il tipo del valore e di schiaffarlo correttamente nella struttura.

Al momento la struttura che contiene tutte le coppie nome-valore è un array, il che limita di parecchio le prestazioni di ricerca. Prevedo di farla diventare al più presto una HashMap, che magari mi costa un pelo di più da creare la prima volta, ma poi è una scheggia da utilizzare durante la ricerca.
Il che mi permetterebbe anche di sbarazzarmi di ResponseMember e tenermi solo il wrapper del valore  :think: ... not bad.


medel è il nuovo burdisso

BeerBaron

A questo punto voglio di più che non la configurazione del forum, voglio cominciare a recuperare un po' di contenuti.
Spezzo l'app in due Activity (l'Activity è il concettualmente la "schermata" di una app con dietro tutta la sua logica, in Android), nella prima farò la login e nella seconda mostrerò i dati dell'utente che si è loggato.

A questo punto, però, comincio a rompermi il cazzo di dovermi smazzare il parsing dei file xml ricevuti, per cui mi cerco una bella libreria XML-RPC che sia già funzionate. E la trovo, proprio per Android, qui: http://code.google.com/p/android-xmlrpc/

Includo sta libreira, creo un paio di classi utili solo a contenere le "define" dei metodi e dei parametri e provo a lanciare una login.

Il risultato è che l'app si pianta dopo qualche secondo e Android decide di killarla senza pietà.
Questo perchè banalmente sto eseguendo operazioni di networking nel contesto principale della UI, cosa vietata dalle policy di Android, proprio perchè il task di gestione della UI non deve subire rallentamenti non dipendenti da esso (ad esempio, appunto, il lag introdotto dalle richieste in rete che possono durare molti secondi).

Per cui la soluzione a questo tipo di situazione è quella di usare un AsyncTask, che è uno strumento molto comodo delle SDK di Andorid per separare la logica pesante e lenta di una Activity dal contesto della UI, e delegarlo ad una esecuzione in background.

L'AsyncTask ha dei metodi che possono essere ridefiniti:


@Override
protected Void doInBackground(TaskParameters... params) {
...
}

doInBackground può praticamente fare tutte le porcate che vuole, visto che gira in background e non rallenta il resto del mondo. può comunicare via rete, leggere dalla memoria esterna, aspettare un segnale... di ogni...


@Override
protected void onPostExecute(Void result) {
...
}

Questo invece può andare ad accedere agli elementi della UI (senza durare troppo, occhio!) e sostanzialmente viene customizzato per aggiornare la schermata con i dati ottenuti dalle elaborazioni in background.

Quindi, ho creato un task che in background mandasse la richiesta di login e si smazzasse i dati ricevuti dall'operazione. Alla ricezione dei dati, viene chiusa la Activity di login e avviata la seconda Activity, quella che mostra il profilo.

Per avviare una Activity da un'altra Activity è sufficiente far partire un Intent (nell'esempio, passandosi anche dei parametri, in modo che la seconda Activity possa avere già in pancia i dati ottenuti dalla prima)


Intent i = new Intent(MainActivity.this, ProfileActivity.class);
Bundle b = new Bundle();
b.putSerializable("results", result);
i.putExtras(b);
startActivity(i);


Nella seconda Activity, invece, riutilizzo il concetto di task per caricare l'immagine del profilo.
E non faccio più un cazz, limitandomi a mostrare un po' di info che ho estratto.
medel è il nuovo burdisso

BeerBaron

Tempo di re-engineering, che il codice è un mezzo cesso.

Allora, prima di tutto ho creato una gerarchia di classi per i Task che devono lavorare in background, e li ho sparati in un package di supporto.
C'è il task generico, che esegue una generica richiesta, c'è poi il task che dovrà smazzarsi dei risultati, che è un task generico che in più "sveglia" l'activity quando ha finito. Poi ho creato anche un task specifico per fare Login, che si recupera i cookie e se li salva.

Poi ho riorganizzato le Activity, creando una super-activity già predisposta all'utilizzo dei suddetti task, con un sistema di notifica a messaggi per la ricezione dei dati prodotti dai task.

Adesso che ho una bella struttura compatta e versatile, posso cominciare a implementare le varie macro-funzionalità di navigazione nel forum... stay tuned!
medel è il nuovo burdisso

Quick Reply

Name:
Email:
Verification:
Please leave this box empty:

Shortcuts: ALT+S post or ALT+P preview