การจัดเก็บข้อมูลทำได้หลายวิธี เช่น ในฐานข้อมูลออนไลน์ ในฐานข้อมูล SQLite ในเครื่อง หรือแม้แต่ในไฟล์ข้อความ คุณเป็นผู้ตัดสินใจว่าโซลูชันใดเหมาะกับแอปพลิเคชันของคุณมากที่สุด บทเรียนนี้จะแสดงวิธีสร้างตารางเสมือน SQLite ที่สามารถค้นหาข้อความทั้งหมดได้อย่างมีประสิทธิภาพ ตารางจะสร้างขึ้นจากข้อมูลในไฟล์ข้อความที่มีคู่คําและคําจํากัดความในแต่ละบรรทัดในไฟล์
สร้างตารางเสมือน
ตารางเสมือนจะทํางานคล้ายกับตาราง SQLite แต่อ่านและเขียนออบเจ็กต์ในหน่วยความจําผ่านคอลแบ็กแทนการเขียนไปยังไฟล์ฐานข้อมูล หากต้องการสร้างตารางเสมือน ให้สร้างคลาสสำหรับตาราง โดยทำดังนี้
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); } }
สร้างคลาสภายในใน DatabaseTable
ที่ขยาย SQLiteOpenHelper
คลาส SQLiteOpenHelper
จะกำหนดเมธอดนามธรรมที่คุณต้องลบล้างเพื่อให้ระบบสร้างและอัปเกรดตารางฐานข้อมูลได้เมื่อจำเป็น ตัวอย่างเช่น นี่เป็นโค้ดบางส่วนที่ประกาศตารางฐานข้อมูลที่จะมีคำสำหรับแอปพจนานุกรม
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); } } }
ป้อนข้อมูลในตารางเสมือน
ตอนนี้ตารางต้องมีข้อมูลที่จะจัดเก็บ โค้ดต่อไปนี้แสดงวิธีอ่านไฟล์ข้อความ (อยู่ใน res/raw/definitions.txt
) ที่มีคําและคําจํากัดความ วิธีแยกวิเคราะห์ไฟล์นั้น และวิธีแทรกแต่ละบรรทัดของไฟล์นั้นเป็นแถวในตารางเสมือน การดำเนินการทั้งหมดนี้ทำในอีกเธรดเพื่อป้องกันไม่ให้ UI ล็อก เพิ่มโค้ดต่อไปนี้ลงในคลาสภายในDatabaseOpenHelper
เคล็ดลับ: คุณอาจต้องตั้งค่าการเรียกกลับเพื่อแจ้งให้ UI ทราบถึงการดำเนินการที่เสร็จสมบูรณ์ของชุดข้อความนี้
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); }
เรียกใช้เมธอด loadDictionary()
ตามความเหมาะสมเพื่อป้อนข้อมูลในตาราง ตำแหน่งที่เหมาะสมคือในเมธอด onCreate()
ของคลาส DatabaseOpenHelper
ทันทีหลังจากสร้างตาราง
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(); }
ค้นหาข้อความค้นหา
เมื่อสร้างและป้อนข้อมูลตารางเสมือนแล้ว ให้ใช้การค้นหาที่ SearchView
ระบุเพื่อค้นหาข้อมูล เพิ่มเมธอดต่อไปนี้ลงในคลาส DatabaseTable
เพื่อสร้างคำสั่ง SQL ที่ค้นหาข้อความค้นหา
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; }
ค้นหาข้อความค้นหาโดยเรียกใช้ getWordMatches()
ระบบจะแสดงผลลัพธ์ที่ตรงกันทั้งหมดใน Cursor
ที่คุณวนซ้ำหรือใช้เพื่อสร้าง ListView
ได้
ตัวอย่างนี้จะเรียก getWordMatches()
ในเมธอด handleIntent()
ของกิจกรรมที่ค้นหาได้ โปรดทราบว่ากิจกรรมที่ค้นหาได้จะรับการค้นหาภายใน Intent ACTION_SEARCH
เป็นข้อมูลเพิ่มเติม เนื่องจากตัวกรอง Intent ที่คุณสร้างขึ้นก่อนหน้านี้
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 } }