Có nhiều cách để lưu trữ dữ liệu của bạn, chẳng hạn như trong cơ sở dữ liệu trực tuyến, trong SQLite cục bộ cơ sở dữ liệu hoặc thậm chí trong một tệp văn bản. Chính bạn là người quyết định đâu là giải pháp tốt nhất cho . Bài học này sẽ hướng dẫn bạn cách tạo một bảng ảo SQLite có thể cung cấp tìm kiếm toàn bộ văn bản. Bảng này được điền sẵn dữ liệu từ tệp văn bản có chứa từ và cặp định nghĩa trên mỗi dòng trong tệp.
Tạo Bảng ảo
Bảng ảo hoạt động tương tự như bảng SQLite, nhưng có thể đọc và ghi vào đối tượng trong bộ nhớ thông qua các lệnh gọi lại, thay vì truyền qua tệp cơ sở dữ liệu. Để tạo bảng ảo, hãy tạo một lớp cho bảng:
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); } }
Tạo một lớp bên trong trong DatabaseTable
, lớp này mở rộng SQLiteOpenHelper
. Lớp SQLiteOpenHelper
xác định các phương thức trừu tượng mà bạn phải ghi đè để có thể tạo bảng cơ sở dữ liệu và
được nâng cấp khi cần thiết. Ví dụ: đây là một số mã khai báo bảng cơ sở dữ liệu sẽ
chứa các từ cho ứng dụng từ điển:
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); } } }
Điền bảng ảo
Bảng hiện cần dữ liệu để lưu trữ. Mã sau đây cho bạn biết cách đọc tệp văn bản
(nằm ở res/raw/definitions.txt
) có chứa các từ và định nghĩa của chúng, cách
để phân tích cú pháp tệp đó và cách chèn từng dòng của tệp đó dưới dạng một hàng trong bảng ảo. Chiến dịch này
đều được thực hiện trong một luồng khác để ngăn giao diện người dùng bị khoá. Thêm mã sau vào
DatabaseOpenHelper
lớp bên trong.
Mẹo: Bạn cũng có thể thiết lập một lệnh gọi lại để thông báo cho giao diện người dùng hoạt động khi hoàn tất chuỗi này.
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); }
Gọi phương thức loadDictionary()
khi thích hợp để điền vào bảng. Đáp
địa điểm tốt sẽ là ở onCreate()
của lớp DatabaseOpenHelper
, ngay sau khi bạn tạo bảng:
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(); }
Tìm kiếm Truy vấn
Sau khi bạn tạo và điền bảng ảo, hãy sử dụng truy vấn do SearchView
cung cấp để tìm kiếm dữ liệu. Thêm các phương thức sau vào phương thức
Lớp DatabaseTable
để tạo một câu lệnh SQL giúp tìm kiếm truy vấn:
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; }
Tìm kiếm truy vấn bằng cách gọi getWordMatches()
. Mọi kết quả trùng khớp đều được trả về
trong Cursor
mà bạn có thể lặp lại hoặc sử dụng để tạo ListView
.
Ví dụ này gọi getWordMatches()
trong phương thức handleIntent()
của thuộc tính có thể tìm kiếm
của bạn. Hãy nhớ rằng hoạt động có thể tìm kiếm sẽ nhận được truy vấn bên trong ý định ACTION_SEARCH
dưới dạng bổ sung, nhờ bộ lọc ý định mà bạn
được tạo trước đó:
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 } }