保存并搜索数据

编写:Lin-H - 原文:http://developer.android.com/training/search/search.html

有很多方法可以储存你的数据,比如储存在线上的数据库,本地的SQLite数据库,甚至是文本文件。你自己来选择最适合你应用的存储方式。本节课程会向你展示如何创建一个健壮的可以提供全文搜索的SQLite虚拟表。并从一个每行有一组单词-解释对的文件中将数据填入。

创建虚拟表

虚拟表与SQLite表的运行方式类似,但虚拟表是通过回调来向内存中的对象进行读取和写入,而不是通过数据库文件。要创建一个虚拟表,首先为该表创建一个类:

  1. public class DatabaseTable {
  2. private final DatabaseOpenHelper mDatabaseOpenHelper;
  3. public DatabaseTable(Context context) {
  4. mDatabaseOpenHelper = new DatabaseOpenHelper(context);
  5. }
  6. }

DatabaseTable类中创建一个继承SQLiteOpenHelper的内部类。你必须重写类SQLiteOpenHelper中定义的abstract方法,才能在必要的时候创建和更新你的数据库表。例如,下面一段代码声明了一个数据库表,用来储存字典app所需的单词。

  1. public class DatabaseTable {
  2. private static final String TAG = "DictionaryDatabase";
  3. //字典的表中将要包含的列项
  4. public static final String COL_WORD = "WORD";
  5. public static final String COL_DEFINITION = "DEFINITION";
  6. private static final String DATABASE_NAME = "DICTIONARY";
  7. private static final String FTS_VIRTUAL_TABLE = "FTS";
  8. private static final int DATABASE_VERSION = 1;
  9. private final DatabaseOpenHelper mDatabaseOpenHelper;
  10. public DatabaseTable(Context context) {
  11. mDatabaseOpenHelper = new DatabaseOpenHelper(context);
  12. }
  13. private static class DatabaseOpenHelper extends SQLiteOpenHelper {
  14. private final Context mHelperContext;
  15. private SQLiteDatabase mDatabase;
  16. private static final String FTS_TABLE_CREATE =
  17. "CREATE VIRTUAL TABLE " + FTS_VIRTUAL_TABLE +
  18. " USING fts3 (" +
  19. COL_WORD + ", " +
  20. COL_DEFINITION + ")";
  21. DatabaseOpenHelper(Context context) {
  22. super(context, DATABASE_NAME, null, DATABASE_VERSION);
  23. mHelperContext = context;
  24. }
  25. @Override
  26. public void onCreate(SQLiteDatabase db) {
  27. mDatabase = db;
  28. mDatabase.execSQL(FTS_TABLE_CREATE);
  29. }
  30. @Override
  31. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  32. Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
  33. + newVersion + ", which will destroy all old data");
  34. db.execSQL("DROP TABLE IF EXISTS " + FTS_VIRTUAL_TABLE);
  35. onCreate(db);
  36. }
  37. }
  38. }

填入虚拟表

现在,表需要数据来储存。下面的代码会向你展示如何读取一个内容为单词和解释的文本文件(位于res/raw/definitions.txt),如何解析文件与如何将文件中的数据按行插入虚拟表中。为防止UI锁死这些操作会在另一条线程中执行。将下面的一段代码添加到你的DatabaseOpenHelper内部类中。

Tip:你也可以设置一个回调来通知你的UI activity线程的完成结果。

  1. private void loadDictionary() {
  2. new Thread(new Runnable() {
  3. public void run() {
  4. try {
  5. loadWords();
  6. } catch (IOException e) {
  7. throw new RuntimeException(e);
  8. }
  9. }
  10. }).start();
  11. }
  12. private void loadWords() throws IOException {
  13. final Resources resources = mHelperContext.getResources();
  14. InputStream inputStream = resources.openRawResource(R.raw.definitions);
  15. BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
  16. try {
  17. String line;
  18. while ((line = reader.readLine()) != null) {
  19. String[] strings = TextUtils.split(line, "-");
  20. if (strings.length < 2) continue;
  21. long id = addWord(strings[0].trim(), strings[1].trim());
  22. if (id < 0) {
  23. Log.e(TAG, "unable to add word: " + strings[0].trim());
  24. }
  25. }
  26. } finally {
  27. reader.close();
  28. }
  29. }
  30. public long addWord(String word, String definition) {
  31. ContentValues initialValues = new ContentValues();
  32. initialValues.put(COL_WORD, word);
  33. initialValues.put(COL_DEFINITION, definition);
  34. return mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
  35. }

任何恰当的地方,都可以调用loadDictionary()方法向表中填入数据。一个比较好的地方是DatabaseOpenHelper类的onCreate())方法中,紧随创建表之后:

  1. @Override
  2. public void onCreate(SQLiteDatabase db) {
  3. mDatabase = db;
  4. mDatabase.execSQL(FTS_TABLE_CREATE);
  5. loadDictionary();
  6. }

搜索请求

当你的虚拟表创建好并填入数据后,根据SearchView提供的请求搜索数据。将下面的方法添加到DatabaseTable类中,用来创建搜索请求的SQL语句:

  1. public Cursor getWordMatches(String query, String[] columns) {
  2. String selection = COL_WORD + " MATCH ?";
  3. String[] selectionArgs = new String[] {query+"*"};
  4. return query(selection, selectionArgs, columns);
  5. }
  6. private Cursor query(String selection, String[] selectionArgs, String[] columns) {
  7. SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
  8. builder.setTables(FTS_VIRTUAL_TABLE);
  9. Cursor cursor = builder.query(mDatabaseOpenHelper.getReadableDatabase(),
  10. columns, selection, selectionArgs, null, null, null);
  11. if (cursor == null) {
  12. return null;
  13. } else if (!cursor.moveToFirst()) {
  14. cursor.close();
  15. return null;
  16. }
  17. return cursor;
  18. }

调用getWordMatches()来搜索请求。任何符合的结果返回到Cursor中,可以直接遍历或是建立一个ListView。这个例子是在检索activity的handleIntent()方法中调用getWordMatches()。请记住,因为之前创建的intent filter,检索activity会在ACTION_SEARCH intent中额外接收请求作为变量存储:

  1. DatabaseTable db = new DatabaseTable(this);
  2. ...
  3. private void handleIntent(Intent intent) {
  4. if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
  5. String query = intent.getStringExtra(SearchManager.QUERY);
  6. Cursor c = db.getWordMatches(query, null);
  7. //执行Cursor并显示结果
  8. }
  9. }