217 lines
7.8 KiB
Plaintext
217 lines
7.8 KiB
Plaintext
page.title=Storing and Searching for Data
|
|
trainingnavtop=true
|
|
previous.title=Setting Up the Search Interface
|
|
previous.link=setup.html
|
|
next.title=Remaining Backward Compatible
|
|
next.link=backward-compat.html
|
|
|
|
@jd:body
|
|
|
|
<div id="tb-wrapper">
|
|
<div id="tb">
|
|
<h2>This lesson teaches you to</h2>
|
|
|
|
<ul>
|
|
<li><a href="{@docRoot}training/search/search.html#create">Create the Virtual
|
|
Table</a></li>
|
|
|
|
<li><a href="{@docRoot}training/search/search.html#populate">Populate the Virtual
|
|
Table</a></li>
|
|
|
|
<li><a href="{@docRoot}training/search/search.html#search">Search for the Query</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<p>There are many ways to store your data, such as in an online database, in a local SQLite
|
|
database, or even in a text file. It is up to you to decide what is the best solution for your
|
|
application. This lesson shows you how to create a SQLite virtual table that can provide robust
|
|
full-text searching. The table is populated with data from a text file that contains a word and
|
|
definition pair on each line in the file.</p>
|
|
|
|
<h2 id="create">Create the Virtual Table</h2>
|
|
|
|
<p>A virtual table behaves similarly to a SQLite table, but reads and writes to an object in
|
|
memory via callbacks, instead of to a database file. To create a virtual table, create a class
|
|
for the table:</p>
|
|
<pre>
|
|
public class DatabaseTable {
|
|
private final DatabaseOpenHelper mDatabaseOpenHelper;
|
|
|
|
public DatabaseTable(Context context) {
|
|
mDatabaseOpenHelper = new DatabaseOpenHelper(context);
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<p>Create an inner class in <code>DatabaseTable</code> that extends {@link
|
|
android.database.sqlite.SQLiteOpenHelper}. The {@link android.database.sqlite.SQLiteOpenHelper} class
|
|
defines abstract methods that you must override so that your database table can be created and
|
|
upgraded when necessary. For example, here is some code that declares a database table that will
|
|
contain words for a dictionary app:</p>
|
|
<pre>
|
|
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 mDatabaseOpenHelper;
|
|
|
|
public DatabaseTable(Context context) {
|
|
mDatabaseOpenHelper = new DatabaseOpenHelper(context);
|
|
}
|
|
|
|
private static class DatabaseOpenHelper extends SQLiteOpenHelper {
|
|
|
|
private final Context mHelperContext;
|
|
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);
|
|
mHelperContext = 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);
|
|
}
|
|
}
|
|
}
|
|
</pre>
|
|
|
|
<h2 id="populate">Populate the Virtual Table</h2>
|
|
|
|
<p>The table now needs data to store. The following code shows you how to read a text file
|
|
(located in <code>res/raw/definitions.txt</code>) that contains words and their definitions, how
|
|
to parse that file, and how to insert each line of that file as a row in the virtual table. This
|
|
is all done in another thread to prevent the UI from locking. Add the following code to your
|
|
<code>DatabaseOpenHelper</code> inner class.</p>
|
|
|
|
<p class="note"><strong>Tip:</strong> You also might want to set up a callback to notify your UI
|
|
activity of this thread's completion.</p>
|
|
<pre>
|
|
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 = mHelperContext.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 mDatabase.insert(FTS_VIRTUAL_TABLE, null, initialValues);
|
|
}
|
|
</pre>
|
|
|
|
<p>Call the <code>loadDictionary()</code> method wherever appropriate to populate the table. A
|
|
good place would be in the {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}
|
|
method of the <code>DatabaseOpenHelper</code> class, right after you create the table:</p>
|
|
<pre>
|
|
@Override
|
|
public void onCreate(SQLiteDatabase db) {
|
|
mDatabase = db;
|
|
mDatabase.execSQL(FTS_TABLE_CREATE);
|
|
loadDictionary();
|
|
}
|
|
</pre>
|
|
|
|
<h2 id="search">Search for the Query</h2>
|
|
|
|
<p>When you have the virtual table created and populated, use the query supplied by your {@link
|
|
android.widget.SearchView} to search the data. Add the following methods to the
|
|
<code>DatabaseTable</code> class to build a SQL statement that searches for the query:</p>
|
|
<pre>
|
|
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(mDatabaseOpenHelper.getReadableDatabase(),
|
|
columns, selection, selectionArgs, null, null, null);
|
|
|
|
if (cursor == null) {
|
|
return null;
|
|
} else if (!cursor.moveToFirst()) {
|
|
cursor.close();
|
|
return null;
|
|
}
|
|
return cursor;
|
|
}
|
|
</pre>
|
|
|
|
<p>Search for a query by calling <code>getWordMatches()</code>. Any matching results are returned
|
|
in a {@link android.database.Cursor} that you can iterate through or use to build a {@link android.widget.ListView}.
|
|
This example calls <code>getWordMatches()</code> in the <code>handleIntent()</code> method of the searchable
|
|
activity. Remember that the searchable activity receives the query inside of the {@link
|
|
android.content.Intent#ACTION_SEARCH} intent as an extra, because of the intent filter that you
|
|
previously created:</p>
|
|
<pre>
|
|
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
|
|
}
|
|
}
|
|
</pre> |