Dopo aver introdotto la piattaforma BaasBox, e le sue caratteristiche di backend, in questa lezione vedremo come realizzare un'app Android che ne fa uso.
L'app d'esempio che realizzeremo è il semplice gestore di spese cui si è accennato nella lezione precedente. Il nostro scopo sarà quindi permettere all'utente di autenticarsi e gestire i propri dati (memorizzati nel database di BaasBox): salvataggio di dati relativi alle spese sostenute (importo, descrizione e tipologia), visualizzazione degli inserimenti fatti (complessivi o relativi ad una categoria), calcolo del saldo totale.
Tutto ciò verrà realizzato mediante l'Android SDK di BaasBox, pertanto ne approfondiremo subito le caratteristiche.
Android SDK per BaasBox
L'Android SDK per BaasBox È realizzato in Java, ed offre classi che mascherano le normali chiamate REST che le API di BaasBox richiedono.
Per lo sviluppo dell'esempio è stato usato Android Studio, ormai strumento ufficiale di lavoro, e la presenza di Gradle al suo interno renderà l'inclusione della libreria ancora più agevole.
Nel build file (build.gradle) relativo al modulo del progetto in cui interagiremo con BaasBox sarà necessario includere l'apposita dipendenza, oltre alla libreria di supporto:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.baasbox:baasbox-android:0.9.0'
}
Le classi offerte permettono di effettuare chiamate in rete in modalità asincrona, sollevandoci dall'onere di creare noi un thread secondario. I risultati delle operazioni richieste saranno restituiti in appositi listener, implementati dalla classe BaasHandler<T>
, ove T
indica il tipo di dato degli oggetti restituiti: ad esempio, una query sui dati fornirà una lista di documenti, e pertanto il listener sarà di tipo BaasHandler< List<BaasDocument> >
.
Un aspetto molto utile - che, per non appesantire troppo l'esempio, non verrà utilizzato - è il RequestToken. Si tratta di un identificatore associato alla richiesta remota che permette di seguirne lo stato anche in quei casi in cui l'Activity viene ricreata. I token sono parcelable, pertanto possono essere salvati con lo stato dell'Activity e ripristinati. A tale scopo, sono dotati di due metodi, denominati suspendAndSave
e loadAndResume
. È importante ricordare che al termine della richiesta asincrona – all'interno del listener corrispondente – il token va posto a null.
Faremo anche uso di altre due classi dell'SDK: BaasUser
, che includerà lo stato dell'utente (dati di profilo, login e logout), e BaasDocument
, per la gestione dei documenti (inserimento e query).
Struttura dell'esempio
Per prima cosa occorre un'installazione funzionante di BaasBox. Nel nostro caso supporremo che essa sia configurata all'indirizzo IP 192.168.233.187, sulla porta TCP 9000. L'appcode rimarrà quello di default, vale a dire 1234567890, e avremo già pronto un utente le cui credenziali (username e password) sono entrambe impostate al valore appclient. Inoltre, disporremo già di una collection vuota di nome gestione_spese.
Tutte queste impostazioni vanno personalizzate sulla propria installazione al momento dell'utilizzo. Collection ed utente sono già stati creati tramite console web solo per semplificare il codice di esempio, ma in un'applicazione reale potranno essere create tramite l'app in base alle necessità.
A livello di configurazione, l'unica necessità consiste nell'inclusione della permission INTERNET
:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="it.html.baasbox.spese" >
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:name=".MyApp"
... >
<activity
android:name=".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>
</manifest>
Come si vede, la nostra app è costituita da un'unica Activity. Questo perchè, per la realizzazione dell'interfaccia, utilizzeremo due Fragment:
-
LoginDialogFragment
: un'estensione di unDialogFragment
che mostrerà una finestra di dialogo per il login; -
MainFragment
: contenente un form per l'inserimento di nuovi dati ed unaListView
che mostra quelli già inseriti.
Un altro sguardo più approfondito al precedente file manifest ci permette di notare che il nodo application
ha un attributo name
impostato a MyApp
. Questo è il nome della classe cui affideremo i nostri dati di accesso a BaasBox:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
BaasBox.builder(this).setAuthentication(BaasBox.Config.AuthType.SESSION_TOKEN)
.setApiDomain("192.168.233.187")
.setPort(9000)
.setAppCode("1234567890")
.setHttpConnectionTimeout(50000)
.init();
}
}
Il codice
L'Activity deriva da AppCompatActivity
- classe che nelle versioni recenti della libreria di supporto ha sostituito la deprecata ActionBarActivity
– e implementa una nostra interfaccia Java, OnLoginListener
:
interface OnLoginListener
{
void onLoggedIn();
void onLoggedOut();
}
Ciò implica che l'Activity deve implementare i metodi esporre tali metodi onLoggedIn()
ed onLoggedOut()
: il primo mostrerà, se necessario, il MainFragment
all'utente, mentre il secondo riporterà alla finestra di login.
Le interazioni tra i Fragment verranno effettuate tramite le FragmentTransaction
.
L'immagine seguente mostra il layout di LoginDialogFragment
:
Al momento della richiesta di accesso, verrà eseguito il seguente codice:
BaasUser user = BaasUser.withUserName(username);
user.setPassword(password);
user.login(onLoggedIn);
dove username
e password
sono le stringhe contenenti le credenziali che l'utente ha digitato. L'oggetto onLoggedIn
è il listener incaricato di attendere la risposta:
private BaasHandler<BaasUser> onLoggedIn=new BaasHandler<BaasUser>() {
@Override
public void handle(BaasResult<BaasUser> result) {
if (result.isFailed()){
txtError.setVisibility(View.VISIBLE);
}
else
{
getDialog().dismiss();
activity.onLoggedIn();
}
}
};
Il valore dell'oggetto result
indicherà se il login ha avuto successo, e solo ciò permetterà di accedere al gestore delle spese.
Nel MainFragment
, i punti centrali del codice riguardano il salvataggio dei dati: la pressione del pulsante Salva creerà un nuovo oggetto BaasDocument
, nelle cui proprietà amount
, description
e category
verranno inclusi i dati inseriti via form:
BaasDocument newdoc=new BaasDocument(COLLECTION_NAME);
newdoc.put("amount",Double.parseDouble(txtAmount.getText().toString()) );
newdoc.put("description", txtDescription.getText().toString() );
newdoc.put("category",spinner.getSelectedItem().toString());
newdoc.save(uploadHandler);
Anche in questo caso la richiesta sarà asincrona e la notifica di conclusione dell'operazione di salvataggio giungerà al listener uploadHandler
:
private final BaasHandler<BaasDocument> uploadHandler = new BaasHandler<BaasDocument>() {
@Override
public void handle(BaasResult<BaasDocument> doc) {
if(doc.isSuccess())
refreshList(filter_check.isChecked());
}
};
Il metodo refreshList
invocato si occupa dell'aggiornamento dei dati mostrati; in pratica, è quello che svolge le interrogazioni sul database:
private void refreshList(boolean filtered) {
if (!filtered)
{
BaasDocument.fetchAll(COLLECTION_NAME, onRefresh);
}
else
{
String[] categories=getResources().getStringArray(R.array.categories);
int position=filter_spinner.getSelectedItemPosition();
BaasQuery.Criteria criteria=BaasQuery.builder()
.where("category=?")
.whereParams(categories[position])
.criteria();
BaasDocument.fetchAll(COLLECTION_NAME,criteria,onRefresh);
}
}
Il valore booleano ricevuto come parametro dirà se la lista deve essere mostrata integralmente o filtrata in base alla categoria che, in quest'ultimo caso, sarà indicata dalla selezione effettuata nello Spinner.
La query verrà eseguita comunque tramite il metodo fetchAll
che, nel caso si voglia effettuare un filtro, riceverà come parametro un oggetto Criteria
paragonabile, come ruolo, ad una clausola WHERE
di SQL.
L'immagine seguente mostra l'app al lavoro al momento di effettuare un filtro di selezione per categoria.
Il codice fin qui descritto è allegato a questa lezione, e liberamente scaricabile.