{"id":12993,"date":"2017-11-27T11:33:10","date_gmt":"2017-11-27T10:33:10","guid":{"rendered":"http:\/\/www.devapp.it\/wordpress\/?p=12993"},"modified":"2017-11-27T11:33:10","modified_gmt":"2017-11-27T10:33:10","slug":"android-app-con-database-interno-guida-passo-passo","status":"publish","type":"post","link":"https:\/\/www.devapp.it\/wordpress\/android-app-con-database-interno-guida-passo-passo\/","title":{"rendered":"Android app con database interno: guida passo passo"},"content":{"rendered":"<p>Una delle caratteristiche che pu\u00f2 rivelarsi molto utile nelle nostre applicazioni Android \u00e8 l&#8217;utilizzo di un database relazionale interno al dispositivo.<\/p>\n<p>In Android, \u00e8 gi\u00e0 compresa la libreria <a href=\"https:\/\/www.sqlite.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">SQLite <\/a>che &#8211; senza attivare alcun servizio continuo in background &#8211; permette di avere a disposizione un <strong>database relazionale salvato in un unico file<\/strong>. Sqlite \u00e8 un motore di persistenza estremamente utilizzato nel mondo proprio perch\u00e8 non richiede alcuna forma id installazione se non l&#8217;integrazione nel linguaggio con cui vogliamo usarlo.<\/p>\n<p>Per avere a disposizione un<strong> database SQLite in un&#8217;app Android<\/strong> sar\u00e0 sufficiente dichiararlo nel codice Java, specificando i <strong>due parametri che permettono di riconoscerlo<\/strong>:<\/p>\n<ul>\n<li>il <em>nome<\/em> del file;<\/li>\n<li>la <em>versione<\/em> di sviluppo: tracceremo il livello di evoluzione di ogni database che progetteremo con un numero intero.<\/li>\n<\/ul>\n<h2>Esempio passo passo<\/h2>\n<p>In questo post, vogliamo fornire un esempio pratico, organizzando per\u00f2 il discorso per gradi in modo da mostrare le varie componenti di cui abbiamo bisogno.<\/p>\n<p><a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/04\/android_database_sqlite_img_01.jpg\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-12998 size-full\" src=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/04\/android_database_sqlite_img_01.jpg\" alt=\"\" width=\"359\" height=\"330\" srcset=\"https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/04\/android_database_sqlite_img_01.jpg 359w, https:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/04\/android_database_sqlite_img_01-300x276.jpg 300w\" sizes=\"auto, (max-width: 359px) 100vw, 359px\" \/><\/a><\/p>\n<p>L&#8217;esempio mostra una semplice app (il cui codice di esempio \u00e8 scaricabile <a href=\"http:\/\/www.devapp.it\/wordpress\/wp-content\/uploads\/2017\/04\/app.zip\">qui<\/a>)che ha lo scopo di memorizzare un elenco di libri cui siamo interessati.<\/p>\n<p>Dovremo gestire <strong>due parti nel progetto<\/strong>:<\/p>\n<ul>\n<li>l&#8217;<strong>interfaccia utente<\/strong> che sar\u00e0 molto semplice e non far\u00e0 altro che mostrare un elenco di libri in una <em>ListView<\/em>;<\/li>\n<li>il<strong> motore di persistenza<\/strong> che sar\u00e0 costituito dalle classi che permettono di interagire con il database.<\/li>\n<\/ul>\n<h2>Gestione del database<\/h2>\n<p>Un modo comodo per gestire il nostro dabatase Sqlite viene offerto direttamente da Android: si tratta della classe <em>SqliteOpenHelper<\/em> che si preoccuper\u00e0 di cercare per noi il database su disco grazie a\u00a0nome e versione che noi indicheremo e di offrircelo nel codice Java, astratto come oggetto di classe <em>SqliteDatabase<\/em>.<\/p>\n<p>Proprio <strong>la classe SqliteDatabase offre al suo interno tutti i metodi per l&#8217;interazione con il database<\/strong>.<\/p>\n<p>Questa \u00e8 la nostra implementazione di SqliteOpenHelper:<\/p>\n<pre class=\"lang:java decode:true\">public class MyHelper extends SQLiteOpenHelper {\r\n\r\n    public MyHelper(Context context, String name, \r\n                    SQLiteDatabase.CursorFactory factory, int version) {\r\n        super(context, name, factory, version);\r\n    }\r\n\r\n    private void inizializza(SQLiteDatabase db)\r\n    {\r\n        String insert1=\"INSERT INTO biblio (titolo, autore, numero_pagine) \" +\r\n                \"VALUES ('Promessi sposi','Alessandro Manzoni',500)\";\r\n        String insert2=\"INSERT INTO biblio (titolo, autore, numero_pagine) \" +\r\n                \"VALUES ('Il deserto dei Tartari','Dino Buzzati', 270)\";\r\n        String insert3=\"INSERT INTO biblio (titolo, autore, numero_pagine) \" +\r\n                \"VALUES ('Il Gattopardo','Giuseppe Tomasi di Lampedusa', 300)\";\r\n\r\n        db.execSQL(insert1);\r\n        db.execSQL(insert2);\r\n        db.execSQL(insert3);\r\n    }\r\n\r\n    @Override\r\n    public void onCreate(SQLiteDatabase db) {\r\n\r\n        String comando=\"CREATE TABLE biblio (\" +\r\n                \"    _id          INTEGER  PRIMARY KEY AUTOINCREMENT,\" +\r\n                \"    titolo      TEXT,\" +\r\n                \"    autore TEXT,\" +\r\n                \"numero_pagine INTEGER)\";\r\n\r\n        db.execSQL(comando);\r\n\r\n        inizializza(db);\r\n    }\r\n\r\n    @Override\r\n    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {\r\n\r\n    }\r\n}<\/pre>\n<p>Come si vede esso richiede obbligatoriamente l&#8217;implementazione di <strong>tre metodi<\/strong>:<\/p>\n<ul>\n<li>un <strong>costruttore<\/strong> che riceva un riferimento al <em>Context<\/em> nonch\u00e9 il nome del database da cercare e la sua versione;<\/li>\n<li><strong>onCreate <\/strong>che viene invocato solo alla prima richiesta del database nella vita dell&#8217;app ossia quando il database ancora non esiste;<\/li>\n<li><strong>onUpgrade <\/strong>invocato quando su disco viene rinvenuto un file dal nome identico a quello richiesto ma di una versione meno recente. Lo lasceremo vuoto nel nostro esempio ma quando viene implementato contiene il codice necessario ad adeguare la struttura del database alla versione pi\u00f9 nuova senza distruggere i dati al suo interno (a meno che questo non sia necessario ai fini dell&#8217;aggiornamento).<\/li>\n<\/ul>\n<p>Nell&#8217;organizzazione del nostro esempio, la classe che user\u00e0 l&#8217;<em>helper<\/em> non sar\u00e0 direttamente l&#8217;Activity bens\u00ec un&#8217;altra, che chiameremo <strong>DbManager<\/strong>, all&#8217;interno della quale inseriremo tutti i metodi che faranno accesso al database.<\/p>\n<pre class=\"lang:java decode:true\">public class DbManager {\r\n\r\n    MyHelper helper=null;\r\n    private final static String DATABASE=\"biblio\";\r\n    private final static int VERSIONE_DATABASE=1;\r\n\r\n    DbManager(Context context)\r\n    {\r\n       \r\n        helper=new MyHelper(context, DATABASE, \r\n                null, VERSIONE_DATABASE);\r\n    }\r\n\r\n    public Cursor elencoLibri()\r\n    {\r\n        String query=\"SELECT * FROM biblio\";\r\n\r\n        SQLiteDatabase db= helper.getReadableDatabase();\r\n        return db.rawQuery(query, null);\r\n    }\r\n\r\n}<\/pre>\n<p>Nel nostro esempio, il DbManager contiene solo un metodo che offre la lista completa dei libri. Si noti che<strong> l&#8217;oggetto che rappresenta il risultato dell&#8217;interrogazione \u00e8 un <em>Cursor<\/em><\/strong>. Questo punta ad un record del set di risultati e dispone di molti metodi:<\/p>\n<ul>\n<li>una serie di <strong>metodi di &#8220;movimento&#8221;<\/strong> permettono di cambiare il record cui stiamo puntando: prima di leggere un record sar\u00e0 necessario raggiungerlo con il Cursor. Tra questi metodi ricordiamo <em>move<\/em>, <em>moveToLast<\/em>, <em>moveToPosition<\/em>, <em>moveNext<\/em> e <em>movePrevious<\/em> che permettono rispettivamente di muoversi di un certo numero di posizioni, di raggiungere il primo o l&#8217;ultimo record, di saltare ad una determinata posizione ed infine di passare al prossimo o al precedente risultato;<\/li>\n<li><strong>metodi per la lettura dei dati<\/strong>. Una volta che il cursore punta ad un record si pu\u00f2 leggere i suoi campi in accordo con il loro tipo di dato e per questo avremo <em>getString<\/em>, <em>getInt<\/em>, <em>getFloat<\/em> e altri ancora.<\/li>\n<\/ul>\n<p>Da notare che i metodi di cui abbiamo parlato al secondo punto del precedente elenco richiedono come parametro in input il numero progressivo all&#8217;interno del record del campo da leggere e non il suo nome. Quindi se dovremo leggere il secondo campo che si chiamer\u00e0 &#8211; poniamo il caso &#8211; &#8220;autore&#8221;, dovremo invocare sul Cursor <em>getString(1)<\/em> e non <em>getString(&#8220;autore&#8221;)<\/em>: ci\u00f2 non sempre aiuta la leggibilit\u00e0 del codice. Potremo, per questo, utilizzare il metodo <em>getColumnIndex,<\/em> sempre della classe Cursor, che convertir\u00e0 il nome del campo nella sua posizione.<\/p>\n<h2>L&#8217;interfaccia utente<\/h2>\n<p>Nell&#8217;Activity, mostreremo i risultati in una semplice ListView che, per semplicit\u00e0, qui creeremo direttamente nell&#8217;<em>onCreate<\/em> e ci focalizzeremo sull&#8217;uso di un particolare tipo di Adapter, specializzato nella lettura di dati da un Cursor: il <em>SimpleCursorAdapter<\/em>.<\/p>\n<pre class=\"lang:java decode:true\">public class MainActivity extends AppCompatActivity {\r\n\r\n    private ListView lv=null;\r\n    private SimpleCursorAdapter adapter=null;\r\n    private DbManager db=null;\r\n\r\n    @Override\r\n    protected void onCreate(Bundle savedInstanceState) {\r\n        super.onCreate(savedInstanceState);\r\n\r\n        lv=new ListView(this);\r\n        setContentView(lv);\r\n\r\n        db=new DbManager(this);\r\n\r\n        adapter=new SimpleCursorAdapter(\r\n                                  this,\r\n                                  R.layout.row_layout,\r\n                                  db.elencoLibri(),\r\n                                  new String[]{\"titolo\",\"autore\",\"numero_pagine\"},\r\n                                  new int[]{R.id.titolo, R.id.autore, R.id.nrpagine},\r\n                                  0\r\n        );\r\n\r\n        lv.setAdapter(adapter);\r\n    }\r\n}<\/pre>\n<p>Conviene prestare un p\u00f2 di attenzione agli \u00a0elementi che richiede il <strong>costruttore del SimpleCursorAdapter<\/strong>, soprattutto dal secondo al quinto:<\/p>\n<ul>\n<li><strong>R.layout.row_layout<\/strong> \u00e8 il layout che disegna la forma che avr\u00e0 ogni singola riga mostrata nella ListView. Al suo interno ci saranno una serie di TextView ognuna delle quali contraddistinta da un <em>id<\/em>: queste ospiteranno i dati letti da un singolo record prelevato dal <em>Cursor<\/em>;<\/li>\n<li><strong>db.elencoLibri()<\/strong> \u00e8 il Cursor con cui inizializzeremo l&#8217;adapter all&#8217;avvio dell&#8217;app;<\/li>\n<li><strong>new String[]{&#8220;titolo&#8221;,&#8221;autore&#8221;,&#8221;numero_pagine&#8221;}<\/strong> \u00e8 l&#8217;elenco dei campi che dovremo leggere dal Cursor;<\/li>\n<li><strong>new int[]{R.id.titolo, R.id.autore, R.id.nrpagine}<\/strong> \u00e8 l&#8217;elenco degli id delle TextView dichiarati in <em>R.layout.row_layout<\/em>.<\/li>\n<\/ul>\n<p>Il <strong>SimpleCursorAdapter assocer\u00e0 da solo i valori prelevati dai campi del record con le TextView<\/strong> e la regola per l&#8217;associazione sar\u00e0 la corrispondenza tra il campo nominato in una posizione dell&#8217;array di String con la TextView il cui id \u00e8 collocato nella corrispondente posizione nell&#8217;array di int.<\/p>\n<p>Questo \u00e8 il row_layout che abbiamo disegnato per l&#8217;esempio ma, ovviamente, si pu\u00f2 modificare a proprio piacimento purch\u00e8 si mantenga una corretta associazione di id e campi del record nella configurazione del SimpleCursorAdapter:<\/p>\n<pre class=\"lang:xhtml decode:true \">&lt;RelativeLayout\r\n    xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\r\n    android:layout_width=\"match_parent\"\r\n    android:layout_height=\"wrap_content\"\r\n    android:paddingLeft=\"@dimen\/paddingL\"&gt;\r\n\r\n    &lt;TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"@dimen\/mediumFont\"\r\n        android:textStyle=\"bold\"\r\n        android:id=\"@+id\/titolo\"\/&gt;\r\n    &lt;TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:layout_below=\"@id\/titolo\"\r\n        android:textSize=\"@dimen\/mediumFont\"\r\n        android:id=\"@+id\/autore\"\/&gt;\r\n    &lt;LinearLayout\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"match_parent\"\r\n        android:orientation=\"vertical\"\r\n        android:background=\"@color\/colorBackground\"\r\n        android:layout_alignParentRight=\"true\"\r\n        android:layout_centerVertical=\"true\"&gt;\r\n    &lt;TextView\r\n        android:layout_width=\"wrap_content\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"@dimen\/bigFont\"\r\n        android:gravity=\"center\"\r\n        android:textColor=\"@android:color\/white\"\r\n        android:id=\"@+id\/nrpagine\"\/&gt;\r\n     &lt;TextView\r\n        android:layout_width=\"match_parent\"\r\n        android:layout_height=\"wrap_content\"\r\n        android:textSize=\"@dimen\/smallFont\"\r\n        android:textStyle=\"bold\"\r\n        android:gravity=\"center\"\r\n        android:textColor=\"@android:color\/white\"\r\n        android:text=\"pagine\"\/&gt;\r\n    &lt;\/LinearLayout&gt;\r\n\r\n&lt;\/RelativeLayout&gt;<\/pre>\n<h2>Conclusioni<\/h2>\n<p>Per il momento, ci fermiamo qui. Questo \u00e8 il primo passo per l&#8217;integrazione di un database Sqlite all&#8217;interno di un&#8217;app Android. Abbiamo cercato di evidenziare in modo lineare i passaggi: prossimamente, approfondiremo il discorso aggiungendo ulteriori funzionalit\u00e0 in modo da completare l&#8217;applicazione.<\/p>\n<p>Continuate a seguirci!<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Una delle caratteristiche che pu\u00f2 rivelarsi molto utile nelle nostre applicazioni Android \u00e8 l&#8217;utilizzo di un database&#8230;<\/p>\n","protected":false},"author":561,"featured_media":13337,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1682,1],"tags":[1278,1562,1530,102],"class_list":["post-12993","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","category-tutorial-pratici","tag-android","tag-creare-app-android","tag-database","tag-sqlite"],"acf":[],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12993","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/users\/561"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/comments?post=12993"}],"version-history":[{"count":6,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12993\/revisions"}],"predecessor-version":[{"id":13338,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/posts\/12993\/revisions\/13338"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media\/13337"}],"wp:attachment":[{"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/media?parent=12993"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/categories?post=12993"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devapp.it\/wordpress\/wp-json\/wp\/v2\/tags?post=12993"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}