Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo almacenar y buscar datos

Existen varias maneras de almacenar tus datos, como en una base de datos en línea, una base de datos SQLite local o incluso un archivo de texto. Tú decides cuál es la mejor opción para tu app. En esta lección, se muestra cómo crear una tabla virtual SQLite que proporcione una búsqueda de texto completo eficiente. La tabla se completa con datos de un archivo de texto que contiene un par de palabra y definición en cada línea.

Cómo crear la tabla virtual

Una tabla virtual se comporta de manera similar a una tabla SQLite, pero lee y escribe en un objeto en la memoria mediante devoluciones de llamadas, y no en un archivo de base de datos. Si quieres crear una tabla virtual, escribe una clase para la tabla:

Kotlin

    class DatabaseTable(context: Context) {

        private val databaseOpenHelper = DatabaseOpenHelper(context)

    }
    

Java

    public class DatabaseTable {
        private final DatabaseOpenHelper databaseOpenHelper;

        public DatabaseTable(Context context) {
            databaseOpenHelper = new DatabaseOpenHelper(context);
        }
    }
    

Crea una clase interna en la DatabaseTable que extienda el SQLiteOpenHelper. La clase del SQLiteOpenHelper define métodos abstractos que debes anular para que tu tabla de base de datos se pueda crear y mejorar cuando sea necesario. Por ejemplo, aquí tienes código que declara una tabla de base de datos que contendrá palabras para una app de diccionario:

Kotlin

    private const val TAG = "DictionaryDatabase"

    //The columns we'll include in the dictionary table
    const val COL_WORD = "WORD"
    const val COL_DEFINITION = "DEFINITION"

    private const val DATABASE_NAME = "DICTIONARY"
    private const val FTS_VIRTUAL_TABLE = "FTS"
    private const val DATABASE_VERSION = 1

    private const val FTS_TABLE_CREATE =
            "CREATE VIRTUAL TABLE $FTS_VIRTUAL_TABLE USING fts3 ($COL_WORD, $COL_DEFINITION)"

    class DatabaseTable(context: Context) {

        private val databaseOpenHelper: DatabaseOpenHelper

        init {
            databaseOpenHelper = DatabaseOpenHelper(context)
        }

        private class DatabaseOpenHelper internal constructor(private val helperContext: Context) :
                SQLiteOpenHelper(helperContext, DATABASE_NAME, null, DATABASE_VERSION) {
            private lateinit var mDatabase: SQLiteDatabase

            override fun onCreate(db: SQLiteDatabase) {
                mDatabase = db
                mDatabase.execSQL(FTS_TABLE_CREATE)
            }

            override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
                Log.w(
                        TAG,
                        "Upgrading database from version $oldVersion to $newVersion , which will " +
                                "destroy all old data"
                )

                db.execSQL("DROP TABLE IF EXISTS $FTS_VIRTUAL_TABLE")
                onCreate(db)
            }

        }
    }
    

Java

    public class DatabaseTable {

        private static final String TAG = "DictionaryDatabase";

        //The columns we'll include in the dictionary table
        public static final String COL_WORD = "WORD";
        public static final String COL_DEFINITION = "DEFINITION";

        private static final String DATABASE_NAME = "DICTIONARY";
        private static final String FTS_VIRTUAL_TABLE = "FTS";
        private static final int DATABASE_VERSION = 1;

        private final DatabaseOpenHelper databaseOpenHelper;

        public DatabaseTable(Context context) {
            databaseOpenHelper = new DatabaseOpenHelper(context);
        }

        private static class DatabaseOpenHelper extends SQLiteOpenHelper {

            private final Context helperContext;
            private SQLiteDatabase mDatabase;

            private static final String FTS_TABLE_CREATE =
                        "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
                        " USING fts3 (" +
                        COL_WORD + ", " +
                        COL_DEFINITION + ")";

            DatabaseOpenHelper(Context context) {
                super(context, DATABASE_NAME, null, DATABASE_VERSION);
                helperContext = context;
            }

            @Override
            public void onCreate(SQLiteDatabase db) {
                mDatabase = db;
                mDatabase.execSQL(FTS_TABLE_CREATE);
            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                        + newVersion + ", which will destroy all old data");
                db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
                onCreate(db);
            }
        }
    }
    

Cómo completar la tabla virtual

La tabla ahora necesita datos para almacenar. El siguiente código muestra cómo leer un archivo de texto (ubicado en res/raw/definitions.txt) que contiene palabras y sus definiciones, cómo analizar ese archivo y cómo insertar cada línea de ese archivo como una fila en la tabla virtual. Todas estas acciones se realizan en otro subproceso a fin de evitar que se bloquee la IU. Agrega el siguiente código a tu clase interna de DatabaseOpenHelper.

Sugerencia: Es posible que también quieras configurar una devolución de llamada para notificarle a tu actividad de IU la finalización del subproceso.

Kotlin

    private fun loadDictionary() {
        Thread(Runnable {
            try {
                loadWords()
            } catch (e: IOException) {
                throw RuntimeException(e)
            }
        }).start()
    }

    @Throws(IOException::class)
    private fun loadWords() {
        val inputStream = helperContext.resources.openRawResource(R.raw.definitions)

        BufferedReader(InputStreamReader(inputStream)).use { reader ->
            var line: String? = reader.readLine()
            while (line != null) {
                val strings: List<String> = line.split("-").map { it.trim() }
                if (strings.size < 2) continue
                val id = addWord(strings[0], strings[1])
                if (id < 0) {
                    Log.e(TAG, "unable to add word: ${strings[0]}")
                }
                line = reader.readLine()
            }
        }
    }

    fun addWord(word: String, definition: String): Long {
        val initialValues = ContentValues().apply {
            put(COL_WORD, word)
            put(COL_DEFINITION, definition)
        }

        return database.insert(FTS_VIRTUAL_TABLE, null, initialValues)
    }
    

Java

    private void loadDictionary() {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        loadWords();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }).start();
        }

    private void loadWords() throws IOException {
        final Resources resources = helperContext.getResources();
        InputStream inputStream = resources.openRawResource(R.raw.definitions);
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

        try {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] strings = TextUtils.split(line, "-");
                if (strings.length < 2) continue;
                long id = addWord(strings[0].trim(), strings[1].trim());
                if (id < 0) {
                    Log.e(TAG, "unable to add word: " + strings[0].trim());
                }
            }
        } finally {
            reader.close();
        }
    }

    public long addWord(String word, String definition) {
        ContentValues initialValues = new ContentValues();
        initialValues.put(COL_WORD, word);
        initialValues.put(COL_DEFINITION, definition);

        return database.insert(FTS_VIRTUAL_TABLE, null, initialValues);
    }
    

Llama al método loadDictionary() cuando corresponda para propagar la tabla. Un buen lugar sería en el método onCreate() de la clase DatabaseOpenHelper, inmediatamente después de crear la tabla:

Kotlin

    override fun onCreate(db: SQLiteDatabase) {
        database = db
        database.execSQL(FTS_TABLE_CREATE)
        loadDictionary()
    }
    

Java

    @Override
    public void onCreate(SQLiteDatabase db) {
        database = db;
        database.execSQL(FTS_TABLE_CREATE);
        loadDictionary();
    }
    

Una vez que hayas creado y propagado la tabla virtual, usa la búsqueda que proporciona la SearchView para encontrar los datos. Agrega los siguientes métodos a la clase DatabaseTable para compilar una SQL statement que encuentre la búsqueda:

Kotlin

    fun getWordMatches(query: String, columns: Array<String>?): Cursor? {
        val selection = "$COL_WORD MATCH ?"
        val selectionArgs = arrayOf("$query*")

        return query(selection, selectionArgs, columns)
    }

    private fun query(
            selection: String,
            selectionArgs: Array<String>,
            columns: Array<String>?
    ): Cursor? {
        val cursor: Cursor? = SQLiteQueryBuilder().run {
            tables = FTS_VIRTUAL_TABLE
            query(databaseOpenHelper.readableDatabase,
                    columns, selection, selectionArgs, null, null, null)
        }

        return cursor?.run {
            if (!moveToFirst()) {
                close()
                null
            } else {
                this
            }
        } ?: null
    }
    

Java

    public Cursor getWordMatches(String query, String[] columns) {
        String selection = COL_WORD + " MATCH ?";
        String[] selectionArgs = new String[] {query+"*"};

        return query(selection, selectionArgs, columns);
    }

    private Cursor query(String selection, String[] selectionArgs, String[] columns) {
        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        builder.setTables(FTS_VIRTUAL_TABLE);

        Cursor cursor = builder.query(databaseOpenHelper.getReadableDatabase(),
                columns, selection, selectionArgs, null, null, null);

        if (cursor == null) {
            return null;
        } else if (!cursor.moveToFirst()) {
            cursor.close();
            return null;
        }
        return cursor;
    }
    

Llama a getWordMatches() para encontrar la búsqueda. Las coincidencias se muestran en un Cursor que puedes iterar o usar para compilar una ListView. En este ejemplo, se llama a getWordMatches() en el método handleIntent() de la actividad que se puede buscar. Recuerda que la actividad que se puede buscar recibe la búsqueda dentro del intent ACTION_SEARCH como un extra, debido al filtro de intents que creaste anteriormente:

Kotlin

    private val db = DatabaseTable(this)

    ...

    private fun handleIntent(intent: Intent) {

        if (Intent.ACTION_SEARCH == intent.action) {
            val query = intent.getStringExtra(SearchManager.QUERY)
            val c = db.getWordMatches(query, null)
            //process Cursor and display results
        }
    }
    

Java

    DatabaseTable db = new DatabaseTable(this);

    ...

    private void handleIntent(Intent intent) {

        if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
            String query = intent.getStringExtra(SearchManager.QUERY);
            Cursor c = db.getWordMatches(query, null);
            //process Cursor and display results
        }
    }