Bienvenido al Tutorial de Ejemplo de Android SQLite. SQLite de Android es la forma más preferida de almacenar datos para aplicaciones de Android. Para muchas aplicaciones, SQLite es la columna vertebral de las aplicaciones, ya sea que se use directamente o a través de algún envoltorio de terceros. A continuación se muestra la aplicación final que crearemos hoy utilizando la base de datos de Android SQLite.
Android SQLite
SQLite de Android es una base de datos muy liviana que viene con el sistema operativo Android. SQLite de Android combina una interfaz SQL limpia con una huella de memoria muy pequeña y una velocidad decente. Para Android, SQLite está “integrado” en el tiempo de ejecución de Android, por lo que cada aplicación de Android puede crear sus propias bases de datos SQLite. La API nativa de SQLite de Android no es JDBC, ya que JDBC podría ser demasiado pesado para un teléfono inteligente con limitaciones de memoria. Una vez que se crea una base de datos con éxito, se encuentra en data/data//databases/ accesible desde el Monitor de Dispositivos Android. SQLite es una base de datos relacional típica, que contiene tablas (que consisten en filas y columnas), índices, etc. Podemos crear nuestras propias tablas para almacenar los datos correspondientemente. Esta estructura se denomina esquema.
Android SQLite SQLiteOpenHelper
Android tiene características disponibles para manejar cambios en los esquemas de bases de datos, que dependen en su mayoría del uso de la clase SQLiteOpenHelper
. SQLiteOpenHelper está diseñado para eliminar dos problemas muy comunes.
- Cuando la aplicación se ejecuta por primera vez – En este punto, aún no tenemos una base de datos. Así que tendremos que crear las tablas, índices, datos iniciales, etc.
- Cuando la aplicación se actualiza a un esquema más reciente – Nuestra base de datos seguirá estando en el antiguo esquema de la edición anterior de la aplicación. Tendremos la opción de alterar el esquema de la base de datos para que coincida con las necesidades del resto de la aplicación.
SQLiteOpenHelper
envuelve esta lógica para crear y actualizar una base de datos según nuestras especificaciones. Para eso, necesitaremos crear una subclase personalizada de SQLiteOpenHelper
implementando al menos los siguientes tres métodos.
-
Constructor : Este toma el Contexto (por ejemplo, una Actividad), el nombre de la base de datos, un cursor opcional (discutiremos esto más adelante) y un entero que representa la versión del esquema de la base de datos que estás utilizando (normalmente empezando desde 1 e incrementando más tarde).
public DatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); }
-
onCreate(SQLiteDatabase db): Se llama cuando no hay una base de datos y la aplicación necesita una. Nos pasa un objeto
SQLiteDatabase
, apuntando a una base de datos recién creada, que podemos poblar con tablas y datos iniciales. -
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion): Se llama cuando la versión del esquema que necesitamos no coincide con la versión del esquema de la base de datos. Nos pasa un objeto SQLiteDatabase y los números de versión antiguos y nuevos. Por lo tanto, podemos averiguar la mejor manera de convertir la base de datos del esquema antiguo al nuevo.
Definimos una clase DBManager
para realizar todas las operaciones CRUD (Crear, Leer, Actualizar y Eliminar) en la base de datos.
Apertura y cierre de la conexión a la base de datos SQLite de Android
Antes de realizar cualquier operación en la base de datos, como insertar, actualizar o eliminar registros en una tabla, primero abra la conexión a la base de datos llamando al método getWritableDatabase() como se muestra a continuación:
public DBManager open() throws SQLException {
dbHelper = new DatabaseHelper(context);
database = dbHelper.getWritableDatabase();
return this;
}
El dbHelper es una instancia de la subclase de SQLiteOpenHelper
. Para cerrar una conexión a la base de datos, se invoca el siguiente método.
public void close() {
dbHelper.close();
}
Inserción de nuevo registro en la tabla de la base de datos SQLite de Android
El siguiente fragmento de código muestra cómo insertar un nuevo registro en la base de datos SQLite de 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 conjunto vacío de valores usando el tamaño inicial dado. Discutiremos los demás valores de instancia cuando pasemos a la parte de codificación.
Actualización de registro en la tabla de la base de datos SQLite de Android
El siguiente fragmento muestra cómo actualizar un único registro.
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 – Eliminando un Registro
Solo necesitamos pasar el id del registro que se va a eliminar, como se muestra a continuación.
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:
- Esto nos permite probar si la consulta devolvió un conjunto vacío (mediante la prueba del valor de retorno)
- Mueve el cursor al primer resultado (cuando el conjunto no está vacío)
El siguiente código se utiliza para recuperar todos los registros:
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;
}
Otra forma de utilizar un Cursor es envolverlo en un CursorAdapter
. Al igual que ArrayAdapter
se adapta a matrices, CursorAdapter
se adapta a objetos Cursor, poniendo sus datos a disposición de un AdapterView
como un ListView
. Vamos a saltar a nuestro proyecto que utiliza SQLite para almacenar algunos datos significativos.
Estructura del Proyecto de Ejemplo de Android SQLite
En esta aplicación, deseamos crear registros que almacenen nombres de países y sus respectivas monedas en forma de un ListView. Cubrimos todas las funciones discutidas anteriormente.
Código del Proyecto Android SQLite
La aplicación consta de 5 clases. Comenzamos definiendo DatabaseHelper, que es una subclase de SQLiteOpenHelper, como sigue: 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 {
// Nombre de la tabla
public static final String TABLE_NAME = "COUNTRIES";
// Columnas de la tabla
public static final String _ID = "_id";
public static final String SUBJECT = "subject";
public static final String DESC = "description";
// Información de la base de datos
static final String DB_NAME = "JOURNALDEV_COUNTRIES.DB";
// Versión de la base de datos
static final int DB_VERSION = 1;
// Consulta para crear la tabla
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);
}
}
Como se discutió anteriormente, hemos anulado los métodos onCreate()
y onUpgrade()
además del constructor. Hemos asignado los nombres a la base de datos y la tabla como JOURNALDEV_COUNTRIES.DB y COUNTRIES respectivamente. La columna de índice se incrementa automáticamente cada vez que se inserta una nueva fila. Los nombres de las columnas para país y moneda son “subject” y “description”. La clase DBManager es donde se inicializa DatabaseHelper y se definen las operaciones CRUD. A continuación, se muestra el código para esta clase: 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 clase CountryListActivity.java
es la actividad que se inicia cuando la aplicación arranca. A continuación se muestra el diseño definido para ella: 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>
Aquí se define un componente ListView para incluir los registros almacenados en la base de datos. Inicialmente, el ListView estaría vacío, por lo tanto, se utiliza un TextView para mostrar lo mismo. 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 Para Elementos de la 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);
}
}
En esta actividad, se invoca al objeto DBManager para realizar las operaciones CRUD. Se define un SimpleCursorAdapter para agregar elementos a la lista a partir de los resultados de la consulta que se devuelven en un objeto Cursor. Al hacer clic en un elemento de la lista, se realiza un intento para abrir la clase ModifyCountryActivity. El menú contiene un elemento para agregar un nuevo registro desde la ActionBar. Aquí nuevamente se realiza un intento para abrir la clase AddCountryActivity. A continuación se muestra el código de 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>
El diseño XML y el código del archivo AddCountryActivity.java
están definidos a continuación: 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>
Se definen dos componentes EditText que toman las entradas para el país y la moneda junto con un botón para agregar los valores a la base de datos y mostrarlos en el 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;
}
}
}
La operación CRUD realizada aquí es agregar un nuevo registro a la base de datos. El diseño XML y el código del archivo ModifyCountryActivity.java están definidos a continuación: 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>
Es similar al diseño anterior excepto que se agregan botones de modificar y eliminar. 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);
}
}
Las operaciones CRUD realizadas aquí son la actualización y eliminación de un registro. Las imágenes a continuación son capturas de pantalla del resultado final de nuestro proyecto. La primera imagen es la salida que se ve cuando se lanza la aplicación por primera vez. La segunda imagen es el resultado de hacer clic en la opción del menú desde la ActionBar para agregar un nuevo registro, como se muestra a continuación.
La tercera imagen muestra la salida cuando se agregan 3 registros:
La cuarta imagen muestra la salida cuando se hace clic en cualquier elemento de la lista para modificar o eliminar un registro:
La imagen final es la salida cuando se elimina un registro. En este ejemplo, eliminamos el primer registro:
Abriendo el archivo de base de datos SQLite de Android
Como discutimos anteriormente en este tutorial, el archivo de la base de datos se almacena en el almacenamiento interno que es accesible desde el Monitor de Dispositivos Android, como se muestra en la imagen a continuación. Para ver esta base de datos, necesitamos extraer este archivo del dispositivo a nuestro escritorio. Esto se hace haciendo clic en la opción de menú en la parte superior derecha, como se ve en la imagen a continuación:
Para abrir este archivo, descargue SQLiteBrowser desde este enlace. Los fragmentos a continuación muestran el esquema y las tablas en el navegador.
Para ver la tabla, vaya a la pestaña “Browse Data” en la parte superior. Se ve la siguiente imagen:
Esto pone fin al tutorial de SQLite de Android. El Proyecto final de SQLite de Android se puede descargar desde el siguiente enlace.
Source:
https://www.digitalocean.com/community/tutorials/android-sqlite-database-example-tutorial