Database SQLite e Datalogger in Android

Nel post di oggi andremo ad analizzare nel dettaglio i passi da seguire ai fini di realizzare un DataBase SQLite in Android Studio. In particolare il DB da noi realizzato si occuperà di salvare ad intervalli di tempo regolari informazioni in merito a temperatura e tempo, che verranno poi utilizzati per la creazione di un grafico.
Ma andiamo con ordine, per prima cosa aprite un vostro progetto o createne uno nuovo e aggiungete al suo interno una nuova classe che estenda SQLiteOpenHelper come nell’esempio qua sotto

public class DatabaseHelper extends SQLiteOpenHelper { }

A questo punto il compilatore vi obbligherà ad inserire i metodi della classe SQLiteOpenHelper quali il costruttore, onCreate() e onUpgrade(). Il primo si occuperà di creare il DB fisico locale nonchè l’oggetto DatabaseHelper ( II° parametro vuole in ingresso il nome del DataBase ), il secondo si occuperà di creare la tabella all’interno del DB, mentre il terzo si occuperà di fare alcuni accorgimenti nel caso in cui venga modificato il DB fisico locale ( ma non lo tratteremo in questa guida ). La tabella di nome temperature_table che andremo a realizzare sarà composta da tre colonne:

ID Temperatura Tempo
1 23.285 1597753508
2 23.324 1597753510

Per realizzare il tutto a livello software:

private static final String DB_name = "DB_CHART.db";
private static final String TABLE_name = "temperature_table";
private static final String COL_1 = "ID";
private static final String COL_2 = "Temperature";
private static final String COL_3 = "Time";

public DatabaseHelper(@Nullable Context context) {
  super(context, DB_name, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
   String CreationQuery = "CREATE TABLE "+TABLE_name+" ( " +
    COL_1 + " INTEGER PRIMARY KEY AUTOINCREMENT," +
    COL_2 + " REAL," +
   COL_3 + " INTEGER )";
    db.execSQL(CreationQuery);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 db.execSQL("DROP TABLE IF EXISTS "+ TABLE_name);
    onCreate(db);
}

Inseriamo i metodi per la lettura, scrittura e cancellazione ( di tutti i dati all’interno della tabella con ripristino ID al valore iniziale, metodo utile solo in questo contesto ).

/**
 * Metodo per scrivere sulla table del Database
 * @param Temperature : temperatura ( double )
 * @param Time : tempo trascorso ( long integer ) - timestamp unix
 * @return true se l'operazione è andata a buon fine, false altrimenti
*/
public boolean saveNewRow(double Temperature, long Time){
    boolean getResult;
    //Ottengo il DB in modalità scrittura
SQLiteDatabase mioDB = this.getWritableDatabase();
//Creo una nuova mappa di valori, in cui i nomi delle colonne sono le chiavi
ContentValues cv = new ContentValues();
//nomeColonna / valore
cv.put("Temperature",Temperature);
cv.put("Time",Time);
//Eseguo l'inserimento nella tabella
try{
mioDB.insert(TABLE_name,null,cv);
getResult = true;
}catch(SQLException sqle){
//Caso in cui l'inserimento non è andato a buon fine
sqle.printStackTrace();
getResult = false;
}
//Chiudo il db ( aperto in modalità scrittura )
   mioDB.close();
    return getResult;
}

/**
* Metodo per ottenere tutti i dati presenti all'interno della tabella temperature_table
* ( anche se alla fine tengo conto solo delle colonne Time e Temperature )
* @return : DataPoint[]
*/
public DataPoint[] getAllData(){
//Ottengo il DB in modalità lettura
SQLiteDatabase mioDB = this.getReadableDatabase();
//Creo un oggetto Cursor per selezionare tutti i dati all'interno della tabella
Cursor mioCursor = mioDB.rawQuery("SELECT * FROM "+TABLE_name,null);
//Istanzio DataPoint di dimensione pari a mioCursore.getCount() ( ovvero pari al numero di
// righe )
DataPoint[] mieiDataPoint = new DataPoint[mioCursor.getCount()];
//Con questo ciclo creo i vari datapoint (x,y) e prendo min e max di entrambi gli assi
for(int i = 0; i < mioCursor.getCount(); i++){
mioCursor.moveToNext();
if (i == 0){
MinX = mioCursor.getInt(mioCursor.getColumnIndex("Time"));
MinY = mioCursor.getDouble(mioCursor.getColumnIndex("Temperature"));
}
if(i == (mioCursor.getCount()-1)){
MaxX = mioCursor.getInt(mioCursor.getColumnIndex("Time"));
MaxY = mioCursor.getDouble(mioCursor.getColumnIndex("Temperature"));
}
mieiDataPoint[i] = new DataPoint(mioCursor.getInt(mioCursor.getColumnIndex("Time")),mioCursor.getDouble(mioCursor.getColumnIndex("Temperature")));
}
//Chiudo il db ( aperto in modalità lettura )
mioDB.close();
return mieiDataPoint;
}

/**
 * Metodo per cancellare tutte le righe del database, riporta anche l'autoincrement ( ID ) al
 * valore iniziale
 */
public void eraseEverything(){
//Ottengo il DB in modalità scrittura
SQLiteDatabase mioDB = this.getWritableDatabase();
//Elimino tutte le righe
mioDB.execSQL("DELETE FROM "+TABLE_name);
//Ripristino ad 1 l'autoincrement ID
mioDB.execSQL("UPDATE SQLITE_SEQUENCE SET SEQ=0 WHERE NAME='"+TABLE_name+"'");
//Chiudo il DB ( aperto in modalità scrittura )
mioDB.close();
}

Nel nostro caso con il metodo getAllData() estraiamo i dati e li passiamo ad un vettore di DataPoint[ ] in quanto il loro utilizzo è limitato solo alla creazione di un grafico, nulla vieta che i dati possano essere estratti e passati a variabili di altro tipo. Il consiglio è quello di non passare mai l’oggetto Cursor ( anche se l’applicativo funzionerebbe comunque ). Ricordarsi sempre di chiudere il DB ( aperto in modalità lettura/scrittura ).
Adesso non rimane altro che creare l’oggetto DatabaseHelper dove vi serve per avere accesso a tutti i metodi.

//Creazione oggetto mioDBHelper nell'onCreate() della main activity
mioDBHelper = new DatabaseHelper(this);