Benvenuti al Tutorial sull’esempio di Android SQLite. Android SQLite è il modo preferito per archiviare dati nelle applicazioni Android. Per molte applicazioni, SQLite è la colonna vertebrale delle app, che venga utilizzato direttamente o tramite un wrapper di terze parti. Di seguito è riportata l’app finale che creeremo oggi utilizzando il database Android SQLite.
Android SQLite
Android SQLite è un database molto leggero fornito con Android OS. Android SQLite combina un’interfaccia SQL pulita con un’occupazione di memoria molto ridotta e una velocità decente. Per Android, SQLite è “integrato” nell’ambiente di runtime Android, quindi ogni applicazione Android può creare i propri database SQLite. La API nativa di Android SQLite non è JDBC, poiché JDBC potrebbe essere troppo oneroso per uno smartphone con limitata memoria. Una volta creato con successo, il database si trova in data/data//databases/, accessibile da Android Device Monitor. SQLite è un tipico database relazionale, contenente tabelle (che consistono in righe e colonne), indici, ecc. Possiamo creare le nostre tabelle per contenere i dati di conseguenza. Questa struttura è definita come uno schema.
Android SQLite SQLiteOpenHelper
Android ha funzionalità disponibili per gestire la modifica degli schemi del database, che dipendono principalmente dall’uso della classe SQLiteOpenHelper
. SQLiteOpenHelper è progettato per eliminare due problemi molto comuni.
- Quando l’applicazione viene eseguita per la prima volta – In questo momento non abbiamo ancora un database. Quindi dovremo creare le tabelle, gli indici, i dati iniziali, e così via.
- Quando l’applicazione viene aggiornata a uno schema più recente – Il nostro database sarà ancora basato sul vecchio schema della versione precedente dell’app. Avremo l’opzione di modificare lo schema del database per soddisfare le esigenze del resto dell’app.
SQLiteOpenHelper
racchiude questa logica per creare e aggiornare un database secondo le nostre specifiche. Per fare ciò, dovremo creare una sottoclasse personalizzata di SQLiteOpenHelper
implementando almeno i seguenti tre metodi.
-
Costruttore: Questo richiede il contesto (ad esempio, un’Activity), il nome del database, un factory di cursor opzionale (ne parleremo in seguito) e un intero che rappresenta la versione dello schema del database che stai utilizzando (tipicamente partendo da 1 e incrementando successivamente).
public DatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); }
-
onCreate(SQLiteDatabase db): Viene chiamato quando non c’è alcun database e l’app ne ha bisogno. Ci passa un oggetto
SQLiteDatabase
, che punta a un database appena creato, che possiamo popolare con tabelle e dati iniziali. -
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): Viene chiamato quando la versione dello schema di cui abbiamo bisogno non corrisponde alla versione dello schema del database. Ci passa un oggetto SQLiteDatabase e i numeri di versione vecchi e nuovi. Possiamo quindi capire il modo migliore per convertire il database dal vecchio schema al nuovo.
Definiamo una classe DBManager
per eseguire tutte le operazioni CRUD(Create, Read, Update e Delete) del database.
Apertura e Chiusura della Connessione al Database SQLite di Android
Prima di eseguire qualsiasi operazione sul database, come l’inserimento, l’aggiornamento o la cancellazione di record in una tabella, apri prima la connessione al database chiamando il metodo getWritableDatabase() come mostrato di seguito:
public DBManager open() throws SQLException {
dbHelper = new DatabaseHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
Il dbHelper è un’istanza della sottoclasse di SQLiteOpenHelper
. Per chiudere la connessione al database, viene invocato il seguente metodo.
public void close() {
dbHelper.close();
}
Inserimento di un Nuovo Record nella Tabella del Database SQLite di Android
Il seguente snippet di codice mostra come inserire un nuovo record nel database SQLite di Android.
public void insert(String name, String desc) {
ContentValues contentValue = new ContentValues();
contentValue.put(DatabaseHelper.SUBJECT, name);
contentValue.put(DatabaseHelper.DESC, desc);
database.insert(DatabaseHelper.TABLE_NAME, null, contentValue);
}
Content Values crea un set vuoto di valori utilizzando la dimensione iniziale fornita. Discuteremo degli altri valori dell’istanza quando passeremo alla parte del codice.
Aggiornamento di un Record nella Tabella del Database SQLite di Android
Il seguente frammento mostra come aggiornare un singolo record.
public int update(long _id, String name, String desc) {
ContentValues contentValues = new ContentValues();
contentValues.put(DatabaseHelper.SUBJECT, name);
contentValues.put(DatabaseHelper.DESC, desc);
int i = database.update(DatabaseHelper.TABLE_NAME, contentValues, DatabaseHelper._ID + " = " + _id, null);
return i;
}
Android SQLite – Eliminazione di un Record
Dobbiamo solo passare l’id del record da eliminare come mostrato di seguito.
public void delete(long _id) {
database.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper._ID + "=" + _id, null);
}
Android SQLite Cursor
A Cursor represents the entire result set of the query. Once the query is fetched a call to cursor.moveToFirst() is made. Calling moveToFirst() does two things:
- Ci consente di testare se la query ha restituito un insieme vuoto (testando il valore di ritorno)
- Sposta il cursore al primo risultato (quando l’insieme non è vuoto)
Il codice seguente viene utilizzato per recuperare tutti i record:
public Cursor fetch() {
String[] columns = new String[] { DatabaseHelper._ID, DatabaseHelper.SUBJECT, DatabaseHelper.DESC };
Cursor cursor = database.query(DatabaseHelper.TABLE_NAME, columns, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
}
return cursor;
}
Un altro modo per utilizzare un Cursor è avvolgerlo in un CursorAdapter
. Proprio come ArrayAdapter
adatta gli array, CursorAdapter
adatta gli oggetti Cursor, rendendo i loro dati disponibili a una AdapterView
come un ListView
. Passiamo al nostro progetto che utilizza SQLite per memorizzare alcuni dati significativi.
Struttura del Progetto Esempio Android SQLite
In questa applicazione desideriamo creare record che memorizzano i nomi dei Paesi e le rispettive valute sotto forma di un ListView. Copriamo tutte le funzionalità discusse sopra.
Codice del progetto Android SQLite
L’applicazione è composta da 5 classi. Iniziamo definendo DatabaseHelper, che è una sottoclasse di SQLiteOpenHelper come segue: DatabaseHelper.java
package com.journaldev.sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
// Nome della tabella
public static final String TABLE_NAME = "COUNTRIES";
// Colonne della tabella
public static final String _ID = "_id";
public static final String SUBJECT = "subject";
public static final String DESC = "description";
// Informazioni sul database
static final String DB_NAME = "JOURNALDEV_COUNTRIES.DB";
// Versione del database
static final int DB_VERSION = 1;
// Query di creazione della tabella
private static final String CREATE_TABLE = "create table " + TABLE_NAME + "(" + _ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT, " + SUBJECT + " TEXT NOT NULL, " + DESC + " TEXT);";
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
Come discusso sopra, abbiamo sovrascritto i metodi onCreate()
e onUpgrade()
oltre al costruttore. Abbiamo assegnato i nomi al database e alla tabella come JOURNALDEV_COUNTRIES.DB e COUNTRIES rispettivamente. La colonna dell’indice viene autoincrementata ogni volta che viene inserita una nuova riga. I nomi delle colonne per il Paese e la valuta sono “subject” e “description”. La classe DBManager è dove viene inizializzato il DatabaseHelper e vengono definite le operazioni CRUD. Di seguito il codice per questa classe: DBManager.java
package com.journaldev.sqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class DBManager {
private DatabaseHelper dbHelper;
private Context context;
private SQLiteDatabase database;
public DBManager(Context c) {
context = c;
}
public DBManager open() throws SQLException {
dbHelper = new DatabaseHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
public void close() {
dbHelper.close();
}
public void insert(String name, String desc) {
ContentValues contentValue = new ContentValues();
contentValue.put(DatabaseHelper.SUBJECT, name);
contentValue.put(DatabaseHelper.DESC, desc);
database.insert(DatabaseHelper.TABLE_NAME, null, contentValue);
}
public Cursor fetch() {
String[] columns = new String[] { DatabaseHelper._ID, DatabaseHelper.SUBJECT, DatabaseHelper.DESC };
Cursor cursor = database.query(DatabaseHelper.TABLE_NAME, columns, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
}
return cursor;
}
public int update(long _id, String name, String desc) {
ContentValues contentValues = new ContentValues();
contentValues.put(DatabaseHelper.SUBJECT, name);
contentValues.put(DatabaseHelper.DESC, desc);
int i = database.update(DatabaseHelper.TABLE_NAME, contentValues, DatabaseHelper._ID + " = " + _id, null);
return i;
}
public void delete(long _id) {
database.delete(DatabaseHelper.TABLE_NAME, DatabaseHelper._ID + "=" + _id, null);
}
}
La classe CountryListActivity.java
è l’attività che viene avviata quando l’applicazione viene avviata. Di seguito è definito il layout per essa: fragment_emp_list.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:dividerHeight="1dp"
android:padding="10dp" >
</ListView>
<TextView
android:id="@+id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/empty_list_text" />
</RelativeLayout>
Qui è definito un componente ListView per includere i record memorizzati nel database. Inizialmente la ListView sarebbe vuota, quindi viene utilizzato un TextView per visualizzare lo stesso. CountryListActivity.java
package com.journaldev.sqlite;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;
public class CountryListActivity extends ActionBarActivity {
private DBManager dbManager;
private ListView listView;
private SimpleCursorAdapter adapter;
final String[] from = new String[] { DatabaseHelper._ID,
DatabaseHelper.SUBJECT, DatabaseHelper.DESC };
final int[] to = new int[] { R.id.id, R.id.title, R.id.desc };
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_emp_list);
dbManager = new DBManager(this);
dbManager.open();
Cursor cursor = dbManager.fetch();
listView = (ListView) findViewById(R.id.list_view);
listView.setEmptyView(findViewById(R.id.empty));
adapter = new SimpleCursorAdapter(this, R.layout.activity_view_record, cursor, from, to, 0);
adapter.notifyDataSetChanged();
listView.setAdapter(adapter);
// OnCLickListiner per gli elementi della lista
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView > parent, View view, int position, long viewId) {
TextView idTextView = (TextView) view.findViewById(R.id.id);
TextView titleTextView = (TextView) view.findViewById(R.id.title);
TextView descTextView = (TextView) view.findViewById(R.id.desc);
String id = idTextView.getText().toString();
String title = titleTextView.getText().toString();
String desc = descTextView.getText().toString();
Intent modify_intent = new Intent(getApplicationContext(), ModifyCountryActivity.class);
modify_intent.putExtra("title", title);
modify_intent.putExtra("desc", desc);
modify_intent.putExtra("id", id);
startActivity(modify_intent);
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.add_record) {
Intent add_mem = new Intent(this, AddCountryActivity.class);
startActivity(add_mem);
}
return super.onOptionsItemSelected(item);
}
}
In questa attività l’oggetto DBManager viene invocato per eseguire le operazioni CRUD. Viene definito un SimpleCursorAdapter per aggiungere elementi alla lista dai risultati della query che vengono restituiti in un oggetto Cursor. Al clic su un elemento della lista viene eseguito un intento per aprire la classe ModifyCountryActivity. Il menu contiene un elemento per aggiungere un nuovo record dalla ActionBar. Anche qui viene eseguito un intento per aprire la classe AddCountryActivity. Di seguito è riportato il codice di menu.xml
. menu.xml
<menu xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
tools:context="com.example.sqlitesample.MainActivity" >
<item
android:id="@+id/add_record"
android:icon="@android:drawable/ic_menu_add"
android:orderInCategory="100"
android:title="@string/add_record"
app:showAsAction="always"/>
</menu>
Il layout xml e il codice del file AddCountryActivity.java
sono definiti di seguito: activity_add_record.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="20dp" >
<EditText
android:id="@+id/subject_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/enter_title" >
<requestFocus />
</EditText>
<EditText
android:id="@+id/description_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/enter_desc"
android:inputType="textMultiLine"
android:minLines="5" >
</EditText>
<Button
android:id="@+id/add_record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/add_record" />
</LinearLayout>
Sono definiti due componenti EditText che prendono in input il paese e la valuta insieme a un pulsante per aggiungere i valori al database e visualizzarli nella ListView. AddCountryActivity.java
package com.journaldev.sqlite;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class AddCountryActivity extends Activity implements OnClickListener {
private Button addTodoBtn;
private EditText subjectEditText;
private EditText descEditText;
private DBManager dbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Add Record");
setContentView(R.layout.activity_add_record);
subjectEditText = (EditText) findViewById(R.id.subject_edittext);
descEditText = (EditText) findViewById(R.id.description_edittext);
addTodoBtn = (Button) findViewById(R.id.add_record);
dbManager = new DBManager(this);
dbManager.open();
addTodoBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.add_record:
final String name = subjectEditText.getText().toString();
final String desc = descEditText.getText().toString();
dbManager.insert(name, desc);
Intent main = new Intent(AddCountryActivity.this, CountryListActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(main);
break;
}
}
}
L’operazione CRUD eseguita qui è l’aggiunta di un nuovo record al database. Il layout xml e il codice del file ModifyCountryActivity.java sono definiti di seguito: activity_modify_record.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp" >
<EditText
android:id="@+id/subject_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:ems="10"
android:hint="@string/enter_title" />
<EditText
android:id="@+id/description_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/enter_desc"
android:inputType="textMultiLine"
android:minLines="5" >
</EditText>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:weightSum="2"
android:gravity="center_horizontal"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_update" />
<Button
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/btn_delete" />
</LinearLayout>
</LinearLayout>
È simile al layout precedente tranne che vengono aggiunti i pulsanti di modifica ed eliminazione. ModifyCountryActivity.java
package com.journaldev.sqlite;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
public class ModifyCountryActivity extends Activity implements OnClickListener {
private EditText titleText;
private Button updateBtn, deleteBtn;
private EditText descText;
private long _id;
private DBManager dbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle("Modify Record");
setContentView(R.layout.activity_modify_record);
dbManager = new DBManager(this);
dbManager.open();
titleText = (EditText) findViewById(R.id.subject_edittext);
descText = (EditText) findViewById(R.id.description_edittext);
updateBtn = (Button) findViewById(R.id.btn_update);
deleteBtn = (Button) findViewById(R.id.btn_delete);
Intent intent = getIntent();
String id = intent.getStringExtra("id");
String name = intent.getStringExtra("title");
String desc = intent.getStringExtra("desc");
_id = Long.parseLong(id);
titleText.setText(name);
descText.setText(desc);
updateBtn.setOnClickListener(this);
deleteBtn.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_update:
String title = titleText.getText().toString();
String desc = descText.getText().toString();
dbManager.update(_id, title, desc);
this.returnHome();
break;
case R.id.btn_delete:
dbManager.delete(_id);
this.returnHome();
break;
}
}
public void returnHome() {
Intent home_intent = new Intent(getApplicationContext(), CountryListActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(home_intent);
}
}
Le operazioni CRUD eseguite qui consistono nell’aggiornare e eliminare un record. Le immagini seguenti sono gli screenshot dell’output finale del nostro progetto. La prima immagine è l’output visualizzato quando l’applicazione viene lanciata per la prima volta. La seconda immagine è il risultato del clic sull’opzione di menu dalla barra delle azioni per aggiungere un nuovo record come mostrato di seguito.
La terza immagine mostra un output quando vengono aggiunti 3 record:
La quarta immagine mostra l’output quando viene cliccato un elemento della lista per modificare o eliminare un record:
L’immagine finale è l’output quando viene eliminato un record. In questo esempio eliminiamo il primo record:
Apertura del file del database SQLite di Android
Come discusso in precedenza in questo tutorial, il file del database è memorizzato nell’archiviazione interna, accessibile dal Monitor dei dispositivi Android come visibile nella foto qui sotto. Per visualizzare questo database, è necessario estrarre questo file dal dispositivo al nostro desktop. Ciò viene fatto cliccando sull’opzione del menu in alto a destra, come mostrato nell’immagine qui sotto:
Per aprire questo file, scarica SQLiteBrowser da questo link. I frammenti seguenti mostrano lo schema e le tabelle nel browser.
Per visualizzare la tabella, vai alla scheda Sfoglia dati in alto. Viene mostrata l’immagine seguente:
Questo conclude il tutorial su Android SQLite. Il Progetto Android SQLite finale è scaricabile dal link sottostante.
Source:
https://www.digitalocean.com/community/tutorials/android-sqlite-database-example-tutorial