X

Usar nuestra propia Base de Datos SQLite en Android

Comienzo con éste una serie de tutoriales relacionados con la aplicación de la Feria de Albacete 2011 en la que se explicará paso por paso como dotar de diversas características a nuestras aplicaciones.

En este caso explicaré como utilizar una base de datos con datos por defecto, es decir, utilizando un fichero externo de base de datos creado con un editor de bases de datos SQLite.

Tabla de Contenidos

Preparando el fichero de base de datos SQLite

Una vez tengamos nuestro propio fichero de base de datos tendremos que modificarla un poco. Para editar la base de datos utilizaremos la aplicación de código abierto SQLite Database Browser disponible para Windows Mac y Linux. Para profundizar más en el uso de esta aplicación existen multitud de cursos y tutoriales como el que ofrece Guru99.com.

Abrimos nuestro fichero de base de datos y añadimos una nueva tabla llamada ‘android_metadata’. Para ello podemos ejecutar el siguiente código SQL:

CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'es_ES')

Seguidamente insertamos una nueva fila con el texto ‘es_ES’ en la tabla recién creada.

INSERT INTO "android_metadata" VALUES ('es_ES')

Una vez hecho esto tenemos que cambiar el nombre identificativo de las claves primarias de las tablas de nuestra base de datos a ‘_id’ para que de esta manera Android pueda identificarlas. Para ello podemos hacer clic en el botón , y tras seleccionar la tabla que queremos modificar, podremos editar el nombre del campo.

Finalmente nuestra base de datos tiene que tener un aspecto similar al siguiente:

Manejando la Base de Datos desde nuestra Aplicación Android

Para poder tener acceso desde nuestra aplicación al fichero de base de datos éste debe encontrarse en una carpeta dentro de nuestro proyecto llamada ‘assets’. Si ya está creada, copiamos el fichero de base de datos ahí, si no, tendremos que crear la carpeta antes.

El manejo de la base de datos se hará utilizando una clase personalizada que extenderá la clase SQLiteOpenHelper, en la cual podremos incluir todos los métodos relacionados con la base de datos para insertar, actualizar y borrar datos.

Recuerda cambiar en el siguiente código las cadenas «TU_PAQUETE» y «NOMBRE_DE_FICHERO» por el nombre del paquete de tu proyecto, y el nombre del fichero de tu base de datos respectivamente.

public class DBHelper extends SQLiteOpenHelper{
    
    //Ruta por defecto de las bases de datos en el sistema Android
    private static String DB_PATH = "/data/data/TU_PAQUETE/databases/";
    
    private static String DB_NAME = "filename.db";
    
    private SQLiteDatabase myDataBase;
    
    private final Context myContext;
    
    /**
    * Constructor
    * Toma referencia hacia el contexto de la aplicación que lo invoca para
    * poder acceder a los 'assets' y 'resources' de la aplicación.
    * Crea un objeto DBOpenHelper que nos permitirá controlar la apertura de
    * la base de datos.
    * @param context
    */
    public DBHelper(Context context) {
        super(context, DB_NAME, null, 1);
        this.myContext = context;
    }
    
    /**
    * Crea una base de datos vacía en el sistema y la reescribe con nuestro
    * fichero de base de datos.
    * */
    public void createDataBase() throws IOException{
        
        boolean dbExist = checkDataBase();
        
        if(dbExist){
            //la base de datos existe y no hacemos nada.
        }else{
            //Llamando a este método se crea la base de datos vacía en la ruta
            //por defecto del sistema de nuestra aplicación por lo que podremos
            //sobreescribirla con nuestra base de datos.
            this.getReadableDatabase();
            
            try {
                
                copyDataBase();
                
            } catch (IOException e) {
                throw new Error("Error copiando Base de Datos");
            }
        }
        
    }
    
    /**
    * Comprueba si la base de datos existe para evitar copiar siempre el
    * fichero cada vez que se abra la aplicación.
    * @return true si existe, false si no existe
    */
    private boolean checkDataBase(){
        
        SQLiteDatabase checkDB = null;
        
        try{
            
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase
                        .openDatabase(myPath, 
                            null, 
                            SQLiteDatabase.OPEN_READONLY);
            
        }catch(SQLiteException e){
            
            //si llegamos aqui es porque la base de datos no existe todavía.
            
        }
        if(checkDB != null){
            
            checkDB.close();
            
        }
        return checkDB != null ? true : false;
    }
    
    /**
    * Copia nuestra base de datos desde la carpeta assets a la recién creada
    * base de datos en la carpeta de sistema, desde dónde podremos acceder a
    * ella.
    * Esto se hace con bytestream.
    * */
    private void copyDataBase() throws IOException{
        
        //Abrimos el fichero de base de datos como entrada
        InputStream myInput = myContext.getAssets().open(DB_NAME);
        
        //Ruta a la base de datos vacía recién creada
        String outFileName = DB_PATH + DB_NAME;
        
        //Abrimos la base de datos vacía como salida
        OutputStream myOutput = new FileOutputStream(outFileName);
        
        //Transferimos los bytes desde el fichero de entrada al de salida
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }
        
        //Liberamos los streams
        myOutput.flush();
        myOutput.close();
        myInput.close();
        
    }
    
    public void open() throws SQLException{
        
        //Abre la base de datos
        try {
            createDataBase();
        } catch (IOException e) {
            throw new Error("Ha sido imposible crear la Base de Datos");
        }
        
        String myPath = DB_PATH + DB_NAME;
        myDataBase = SQLiteDatabase
                        .openDatabase(myPath, 
                            null, 
                            SQLiteDatabase.OPEN_READONLY);
        
    }
    
    @Override
    public synchronized void close() {
        if(myDataBase != null)
        myDataBase.close();
        super.close();
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        
    }
    /**
    * A continuación se crearán los métodos de lectura, inserción,
    * actualización y borrado de la base de datos.
    * */

Métodos útiles de lectura, inserción, actualización y borrado

A continuación pongo ejemplos de los métodos más habituales de uso de base de datos que deberían introducirse en la misma clase DBHelper. Para ello utilizaré como ejemplo la tabla que puede verse en la imagen del Paso 1, que consta de tres columnas (‘_id’, ‘alarma’ y ‘evento’):

//Establecemos los nombres de las columnas
public static final String KEY_ID = "_id";
public final static String KEY_COL1 = "alarma";
public final static String KEY_COL2 = "evento";

//Array de strings para su uso en los diferentes métodos
private static final String[] cols = new String[] { KEY_ID, KEY_COL1, KEY_COL2 };

Los métodos de inserción, borrado y actualización son bastante simples. Para más información sobre ellos podéis echarle un ojo a la información de la clase SQLiteDatabase.

/**
* INSERTAR NUEVA ALARMA
* */
public long insertAlarma(Integer id, Integer alarma, Integer evento) {
    ContentValues newValues = new ContentValues();
    newValues.put(KEY_ID, id);
    newValues.put(KEY_COL1, alarma);
    newValues.put(KEY_COL2, evento);
    return db.insert(DATABASE_TABLE, null, newValues);
}

/**
* BORRAR ALARMA CON _id = _rowIndex
* */
public boolean removeAlarma(long _rowIndex) {
    return db.delete(DATABASE_TABLE, KEY_ID + "=" + _rowIndex, null) > 0;
}

/**
* ACTUALIZAR ALARMA _id = _rowIndex
* */
public boolean updateAlarma(Integer _rowIndex, Integer alarma, Integer evento) {
    ContentValues newValues = new ContentValues();
    newValues.put(KEY_COL1,alarma);
    newValues.put(KEY_COL2, evento);
    return db.update(DATABASE_TABLE, newValues, KEY_ID + "=" + _rowIndex, null) > 0;
}

Por otro lado, para la lectura de la base de datos, se utiliza el método ‘query’, el cual nos devuelve un cursor con las filas que se corresponden con el criterio de búsqueda. Mi recomendación para evitar el uso de cursores en nuestra aplicación es crear objetos de una clase propia personalizada para el tipo de datos que vamos a recuperar. De este modo nuestros métodos devolverán el tipo de objetos que realmente vamos a utilizar. Para este ejemplo utilizamos el tipo de objeto «Alarma» que contiene los dos campos de la base de datos que queremos utilizar como variables:

public class Alarma {
    
    private Integer Evento;
    private Integer Alarma;
    
    public Alarma(Integer idEvento, Integer idAlarma) {
        super();
        this.idEvento = idEvento;
        this.idAlarma = idAlarma;
    }
    
}

De este modo podemos crear dos tipos de métodos para obtener datos. Uno que nos devuelva un único resultado, y otro que nos devuelva un objeto List<Alarma> con todos los resultados. Para más información acerca del método ‘query’ y crear consultas más avanzadas os recomiendo echarle un ojo a la información de la clase SQLiteDatabase:

public Alarma getAlarma(long _rowIndex) {
    Alarma alarm = new Alarma();
    Cursor result = db.query(true, DATABASE_TABLE,
    cols,
    KEY_ID + "=" + _rowIndex, null, null, null,
    null, null);
    if ((result.getCount() == 0) || !result.moveToFirst()) {
        //Si la alarma no existe, devuelve una alarma con valores -1 y -1
        alarm = new Alarma(-1,-1);
        
    } else {
        if (result.moveToFirst()) {
            alarm = new Alarma(
            result.getInt(result.getColumnIndex(KEY_COL1)),
            result.getInt(result.getColumnIndex(KEY_COL2))
            );
        }
    }
    return alarm;
}

public List<Alarma> getAlarmas() {
    ArrayList<Alarma> alarms = new ArrayList()<Alarma>;
    Cursor result = db.query(DATABASE_TABLE,
    cols, null, null, null, null, KEY_ID);
    if (result.moveToFirst())
    do {
        alarms.add(new Alarma(
        result.getInt(result.getColumnIndex(KEY_COL1)),
        result.getInt(result.getColumnIndex(KEY_COL2))
        )
        );
    } while(result.moveToNext());
    return alarms;
}

Utilizando la clase DBHelper

Una vez tenemos creada la clase DBHelper el uso de la misma es bastante trivial. Basta con crear un objeto DBHelper y llamar a los métodos open() y close() antes y después de utilizar los métodos de uso que hayamos creado. Mi recomendación es llamar al método open() en los métodos onCreate() y onResume() de la actividad que vaya a utilizar la base de datos y close() en el método onPause().

public class miActividad extends Activity {
    
    private DBHelper BD;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //Creamos y abrimos la base de datos
        BD=new DBHelper(this);
        BD.open();
        
        //Insertamos una nueva alarma con valores _id=1, alarma=1, evento=1
        BD.insertAlarma(1, 1, 1);
        
        //Modificamos la alarma anterior dejándola como _id=1, alarma=2, evento=3
        BD.updateAlarma(1, 2, 3);
        
        //Obtenemos la alarma creada anteriormente
        Alarma alarma = BD.getAlarma(1);
        
        //Borramos la alarma creada anteriormente con índice 1
        BD.removeAlarma(1)
        
        //Obtenemos un listado de todas las alarmas
        ArrayList<Alarma> alarmas = new ArrayList()<Alarma>;
        alarmas =(ArrayList<Alarma>) BD.getAlarmas();
        
    }
    
    @Override
    public void onPause() {
        super.onPause();
        BD.close();
    }
    @Override
    public void onResume() {
        super.onResume();
        BD.open();
    }

Si tenéis cualquier consulta, dejadla en un comentario.

Aquí os dejo un ejemplo algo más avanzado que seguro que os resultará de utilidad:

Código Fuente en GitHub

Miguel:

Ver comentarios (72)

  • antes que nada gracias por compartir tus conocimientos con nosotros, pero tengo la siguiente pregunta, como podría hacer para copiar toda una base de datos Sqlite en una carpeta de la sd y que se guarde en formato scv o de perdidas en un archivo txt separado por comas

  • Gracias por el aporte en español, me sirvió mucho en mi proyecto, solo preguntarte una duda, hay alguna forma de Actualizar la base de datos una vez instalada la aplicación con un botón dentro de a misma aplicación, gracias un saludo

    • La clave está en el método copyDataBase(). Es el que se ocupa de leer el fichero desde la carpeta del paquete de la aplicación y guardarlo donde corresponde. Si haces que se llame a ese método mediante un botón puedes hacer que se actualice. Además puedes modificar ese método para que obtenga la base de datos de otra fuente, como puede ser una API REST.

  • Hola, tengo un problema con mi proyecto tengo una Base de Datos con varios registros que solo utilizo para consulta pero al realizar la primer consulta me indica que las tablas estan vacias. Si pudieran indicarme si hay algo que cambiar.

  • hola queria saber como puedo relacionar las tablas de sqlite desde el SQLite Database Browser o no es necesario

    • No es necesario. Con SQLite Database Browser lo que haces es generar el fichero con los datos iniciales que quieras utilizar.

  • Hola en la clase principal pongo el
    Constructor BaseDatos bd=new BaseDatos(this);
    El problema es que me marca error en this dice que no puede ser aplicado.
    Alguna posible solucion.
    Le agradeceria.

  • Buenas! No sé si seguirá alguien pendiente de esta entrada, pero tengo una duda con la codificación de la tabla que me está volviendo loco...

    Creo mi tabla en con SQLite Database Browser, y la primera tabla que creo es la de android_metadata como apuntáis, poniendo como valor por defecto es_ES e insertando una línea con ese mismo valor.

    Después creo el resto de tablas que necesito, y muchas de ellas contienen texto con caracteres especiales (tildes, "ñ", etc.).

    Pues bien, a la hora de crear la BDD en tiempo de ejecución, con una clase que hereda de SQLiteOpenHelper, ME CAMBIA A en_US EL VALOR DE LA TABLA android_metadata y no puedo mostrar en la aplicación esos caracteres especiales.

    ¿Alguna idea de por qué se cambia ese valor? ¿Hay alguna configuración que se me esté escapando?

    (Para desarrollar utilizo el eclipse juno, no sé si será problema del eclipse o yo qué sé..)

    Muchas gracias, y un saludo!

  • Saludos:

    Estoy incursionando en el proceso de las apps no se como podría desarrollar una app para android que realice una consulta en un archivo de Excel local?

    Buena noche
    Mil Gracias por sus profetas respuestas

  • Estoy usando Android Studio, en que parte de la estrutura del proyecto debo de copiar el archivo de la base de datos de sqlite (.db) ???

Entrada Relacionada