Menyimpan dan Menelusuri Data

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.

Ada banyak cara untuk menyimpan data Anda, seperti dalam database online, database SQLite lokal, atau bahkan file teks. Anda berhak memutuskan solusi terbaik untuk aplikasi Anda. Tutorial ini menunjukkan kepada Anda cara membuat tabel virtual SQLite yang bisa menyediakan penelusuran teks lengkap yang kuat. Tabel diisi dengan data dari file teks yang berisi pasangan kata dan definisi pada setiap baris dalam file.

Membuat Tabel Virtual

Tabel virtual berperilaku mirip dengan tabel SQLite, tetapi tabel virtual membaca dan menulis ke objek di memori melalui callback, bukan ke file database. Untuk membuat tabel virtual, buat class untuk tabel:

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);
        }
    }
    

Buat class dalam DatabaseTable yang memperluas SQLiteOpenHelper. Class SQLiteOpenHelper akan mendefinisikan metode abstrak yang harus Anda ganti agar tabel database Anda dapat dibuat dan diupgrade saat diperlukan. Contohnya, berikut adalah beberapa kode yang menyatakan tabel database yang akan berisi kata-kata untuk aplikasi kamus:

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);
            }
        }
    }
    

Mengisi Tabel Virtual

Tabel tersebut sekarang memerlukan data untuk disimpan. Kode berikut menunjukkan cara membaca file teks (terletak di res/raw/definitions.txt) yang berisi kata dan definisinya, cara mem-parsing file tersebut, dan cara menyisipkan setiap baris file tersebut dalam tabel virtual. Ini semua dilakukan di thread lain agar UI tidak terkunci. Tambahkan kode berikut ini ke class dalam DatabaseOpenHelper Anda.

Tips: Anda juga mungkin ingin menyiapkan callback untuk memberi tahu aktivitas UI Anda tentang penyelesaian thread ini.

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);
    }
    

Panggil metode loadDictionary() bila perlu untuk mengisi tabel. Sebaiknya jika berada dalam metode onCreate() dari class DatabaseOpenHelper, tepat setelah Anda membuat tabel:

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();
    }
    

Jika Anda telah membuat dan mengisi tabel virtual, gunakan kueri yang disediakan oleh SearchView untuk menelusuri data. Tambahkan metode berikut ke class DatabaseTable untuk membuat pernyataan SQL yang akan menelusuri kueri:

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;
    }
    

Telusuri kueri dengan memanggil getWordMatches(). Hasil apa pun yang cocok akan dikembalikan dalam Cursor yang dapat Anda iterasi atau gunakan untuk membuat file ListView. Contoh ini akan memanggil getWordMatches() dalam metode handleIntent() dari aktivitas yang dapat ditelusuri. Perlu diingat bahwa aktivitas yang dapat ditelusuri akan menerima kueri di dalam intent ACTION_SEARCH sebagai tambahan karena filter intent yang telah Anda buat sebelumnya:

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
        }
    }