问题
I have a simple application that just uses the hardware search key to search my database via my content provider. After getting the results from the Suggestions and clicking on one of the choices the listview screen appears and it's blank. I get an error:
E/InputQueue-JNI(1137): channel '4150a5d0 PopupWindow:412ccf98 (client)' ~ Publisher closed input channel or an error occurred. events=0x8
I'm new to android development, I did some research, but can't figure out what this even means.
Here's the full logcat:
06-24 14:54:03.154: I/TDAProvider(1137): query
06-24 14:54:03.924: I/TDAProvider(1137): query return cursor
06-24 14:54:04.014: I/TDAProvider(1137): query
06-24 14:54:04.304: I/TDAProvider(1137): query return cursor
06-24 14:54:04.754: D/dalvikvm(1137): GC_CONCURRENT freed 137K, 3% free 9374K/9607K, paused 23ms+28ms
06-24 14:54:07.154: E/InputQueue-JNI(1137): channel '4150a5d0 PopupWindow:412ccf98 (client)' ~ Publisher closed input channel or an error occurred. events=0x8
06-24 14:54:07.464: I/TDAProvider(1137): query
06-24 14:54:07.464: I/TDAProvider(1137): query return cursor
06-24 14:54:07.694: I/dalvikvm(1137): threadid=3: reacting to signal 3
06-24 14:54:07.745: I/dalvikvm(1137): Wrote stack traces to '/data/anr/traces.txt'
Any suggestions on how to either solve this or debug it would be welcome.
Since most of the processing is being done by android classes, I'm not sure what to look at. I'm using 2 uri's 'SEARCH' and 'CONTENT_URI_RULES' from my content provider (TDAProvider.java). My guess is there's an issue in the content provider, but I'm not sure what.
Here's my manifest file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ActivityMain"
>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="64dp"
android:layout_marginTop="22dp"
android:text="Test Search - using hardware button. Search for 'check'"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
Here's my searchable.xml that I put into the xml folder.
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name"
android:searchSettingsDescription="@string/search_description"
android:searchSuggestAuthority="com.birdsall.tda.contentprovidertda"
android:searchSuggestIntentAction="android.intent.action.VIEW"
android:searchSuggestIntentData="content://com.birdsall.tda.TDAProvider/rules" >
</searchable>
Here the ActivitySearch.java that is used to facilitate the search:
package com.birdsall.tda;
import android.app.ListActivity;
import android.app.LoaderManager;
import android.app.SearchManager;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.SimpleCursorAdapter;
public class ActivitySearch extends ListActivity implements
LoaderManager.LoaderCallbacks<Cursor> {
private SimpleCursorAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create a new adapter and bind it to the List View
adapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, null,
new String[] { TDAdb.COL_RULETITLE },
new int[] { android.R.id.text1 }, 0);
setListAdapter(adapter);
// Initiate the Cursor Loader
getLoaderManager().initLoader(0, null, this);
// Get the launch Intent
parseIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
parseIntent(getIntent());
}
private static String QUERY_EXTRA_KEY = "QUERY_EXTRA_KEY";
private void parseIntent(Intent intent) {
// If the Activity was started to service a Search request,
// extract the search query.
if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
String searchQuery = intent.getStringExtra(SearchManager.QUERY);
// Perform the search, passing in the search query as an argument
// to the Cursor Loader
Bundle args = new Bundle();
args.putString(QUERY_EXTRA_KEY, searchQuery);
// Restart the Cursor Loader to execute the new query.
getLoaderManager().restartLoader(0, args, this);
}
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
String query = "0";
if (args != null) {
// Extract the search query from the arguments.
query = args.getString(QUERY_EXTRA_KEY);
}
// Construct the new query in the form of a Cursor Loader.
String[] projection = { TDAdb.KEY_ROWID, TDAdb.COL_RULETITLE };
String where = TDAdb.COL_RULETITLE + " LIKE \"%" + query + "%\"";
String[] whereArgs = null;
String sortOrder = TDAdb.COL_RULETITLE;
// Create the new Cursor loader.
return new CursorLoader(this, TDAProvider.CONTENT_URI_RULES,
projection, where, whereArgs, sortOrder);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Replace the result Cursor displayed by the Cursor Adapter with
// the new result set.
adapter.swapCursor(cursor);
}
public void onLoaderReset(Loader<Cursor> loader) {
// Remove the existing result Cursor from the List Adapter.
adapter.swapCursor(null);
}
}
Here's the content provider (TDAProvider.java), I do user some raw queries, but not for this example:
package com.birdsall.tda;
import java.util.HashMap;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class TDAProvider extends ContentProvider {
private TDAdbHelper dbHelper;
private static final String TAG = "TDAProvider";
private static final int ALL_CHAPTERS = 1;
private static final int SINGLE_CHAPTER = 2;
private static final int ALL_RULES = 3;
private static final int SINGLE_RULE = 4;
private static final int ALL_RAW_QUERY = 5;
private static final int SINGLE_RAW_QUERY = 6;
private static final int SEARCH = 7;
// authority is the symbolic name of your provider
// To avoid conflicts with other providers, you should use
// Internet domain ownership (in reverse) as the basis of your provider
// authority.
private static final String AUTHORITY = "com.birdsall.tda.contentprovidertda";
// create content URIs from the authority by appending path to database
// table
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ "/chapters");
public static final Uri CONTENT_URI_RULES = Uri.parse("content://"
+ AUTHORITY + "/rules");
public static final Uri CONTENT_URI_RAWQUERY = Uri.parse("content://"
+ AUTHORITY + "/rawquery");
// a content URI pattern matches content URIs using wildcard characters:
// *: Matches a string of any valid characters of any length.
// #: Matches a string of numeric characters of any length.
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, "chapters", ALL_CHAPTERS);
uriMatcher.addURI(AUTHORITY, "chapters/#", SINGLE_CHAPTER);
uriMatcher.addURI(AUTHORITY, "rules", ALL_RULES);
uriMatcher.addURI(AUTHORITY, "rules/#", SINGLE_RULE);
uriMatcher.addURI(AUTHORITY, "rawquery", ALL_RAW_QUERY);
uriMatcher.addURI(AUTHORITY, "rawquery/#", SINGLE_RAW_QUERY);
uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,
SEARCH);
uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY
+ "/*", SEARCH);
uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT,
SEARCH);
uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_SHORTCUT
+ "/*", SEARCH);
}
// system calls onCreate() when it starts up the provider.
@Override
public boolean onCreate() {
// get access to the database helper
Log.i(TAG, "onCreate ");
dbHelper = new TDAdbHelper(getContext());
return false;
}
// Return the MIME type corresponding to a content URI
@Override
public String getType(Uri uri) {
Log.i(TAG, "getType ");
switch (uriMatcher.match(uri)) {
case ALL_CHAPTERS:
return "vnd.android.cursor.dir/vnd.com.birdsall.peter.contentprovidertda.chapters";
case SINGLE_CHAPTER:
return "vnd.android.cursor.item/vnd.com.birdsall.peter.contentprovidertda.chapters";
case ALL_RULES:
return "vnd.android.cursor.dir/vnd.com.birdsall.peter.contentprovidertda.rules";
case SINGLE_RULE:
return "vnd.android.cursor.item/vnd.com.birdsall.peter.contentprovidertda.rules";
case ALL_RAW_QUERY:
return "vnd.android.cursor.dir/vnd.com.birdsall.peter.contentprovidertda.rulesdescribed";
case SINGLE_RAW_QUERY:
return "vnd.android.cursor.item/vnd.com.birdsall.peter.contentprovidertda.rulesdescribed";
case SEARCH:
return SearchManager.SUGGEST_MIME_TYPE;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
}
private static final HashMap<String, String> SEARCH_PROJECTION_MAP;
static {
SEARCH_PROJECTION_MAP = new HashMap<String, String>();
SEARCH_PROJECTION_MAP.put(SearchManager.SUGGEST_COLUMN_TEXT_1,
TDAdb.COL_RULETITLE + " AS "
+ SearchManager.SUGGEST_COLUMN_TEXT_1);
SEARCH_PROJECTION_MAP.put("_id", TDAdb.KEY_ROWID + " AS " + "_id");
}
// The insert() method adds a new row to the appropriate table, using the
// values
// in the ContentValues argument. If a column name is not in the
// ContentValues argument,
// you may want to provide a default value for it either in your provider
// code or in
// your database schema.
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i(TAG, "insert ");
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_CHAPTERS:
// do nothing
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
long id = db.insert(TDAdb.CHAPTER_TABLE, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return Uri.parse(CONTENT_URI + "/" + id);
}
// The query() method must return a Cursor object, or if it fails,
// throw an Exception. If you are using an SQLite database as your data
// storage,
// you can simply return the Cursor returned by one of the query() methods
// of the
// SQLiteDatabase class. If the query does not match any rows, you should
// return a
// Cursor instance whose getCount() method returns 0. You should return null
// only
// if an internal error occurred during the query process.
// public Cursor query (Uri uri, String[] projection, String selection,
// String[] selectionArgs,
// String having, String groupby, String sortOrder, String limit,
// CancellationSignal cancellationSignal) {
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String id;
Log.i(TAG, "query ");
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// queryBuilder.setTables(TDAdb.CHAPTER_TABLE);
switch (uriMatcher.match(uri)) {
case ALL_CHAPTERS:
// do nothing
queryBuilder.setTables(TDAdb.CHAPTER_TABLE);
break;
case SINGLE_CHAPTER:
queryBuilder.setTables(TDAdb.CHAPTER_TABLE);
id = uri.getPathSegments().get(1);
queryBuilder.appendWhere(TDAdb.KEY_ROWID + "=" + id);
break;
case ALL_RULES:
// do nothing
queryBuilder.setTables(TDAdb.RULE_TABLE);
break;
case SINGLE_RULE:
queryBuilder.setTables(TDAdb.RULE_TABLE);
id = uri.getPathSegments().get(1);
queryBuilder.appendWhere(TDAdb.KEY_ROWID + "=" + id);
break;
case ALL_RAW_QUERY:
db = dbHelper.getReadableDatabase();
return db.rawQuery(selection, selectionArgs);
case SINGLE_RAW_QUERY:
db = dbHelper.getReadableDatabase();
return db.rawQuery(selection, selectionArgs);
case SEARCH:
queryBuilder.setTables(TDAdb.RULE_TABLE);
queryBuilder.appendWhere(TDAdb.COL_RULETITLE + " LIKE \"%"
+ uri.getPathSegments().get(1) + "%\"");
queryBuilder.setProjectionMap(SEARCH_PROJECTION_MAP);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, null, null, sortOrder);
Log.i(TAG, "query return cursor ");
// Register the contexts ContentResolver to be notified if
// the cursor result set changes.
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
// The delete() method deletes rows based on the seletion or if an id is
// provided then it deleted a single row. The methods returns the numbers
// of records delete from the database. If you choose not to delete the data
// physically then just update a flag here.
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i(TAG, "delete ");
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_CHAPTERS:
// do nothing
break;
case SINGLE_CHAPTER:
String id = uri.getPathSegments().get(1);
selection = TDAdb.KEY_ROWID
+ "="
+ id
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int deleteCount = db.delete(TDAdb.CHAPTER_TABLE, selection,
selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
// The update method() is same as delete() which updates multiple rows
// based on the selection or a single row if the row id is provided. The
// update method returns the number of updated rows.
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
Log.i(TAG, "update ");
SQLiteDatabase db = dbHelper.getWritableDatabase();
switch (uriMatcher.match(uri)) {
case ALL_CHAPTERS:
// do nothing
break;
case SINGLE_CHAPTER:
String id = uri.getPathSegments().get(1);
selection = TDAdb.KEY_ROWID
+ "="
+ id
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection
+ ')' : "");
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
int updateCount = db.update(TDAdb.CHAPTER_TABLE, values, selection,
selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
Log.i(TAG, "update return updateCount ");
return updateCount;
}
}
Here's the database activity (TDAdb.java):
package com.birdsall.tda;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class TDAdb {
public static final String LOG_TAG = "TDAdb";
// tables
public static final String CHAPTER_TABLE = "chapters";
public static final String RULE_TABLE = "rules";
public static final String INDEXHEADER_TABLE = "indexheader";
public static final String KEYDESCRIPTORS_TABLE = "keydescriptors";
public static String SQLITE_TABLE = "chapters";
// Chapter Table Columns
public static final String KEY_ROWID = "_id";
public static final String COL_CHAPTER = "chapter";
public static final String COL_CHAPTERTITLE = "chaptertitle";
// Rule Table Columns
public static final String COL_RULE = "rule";
// public static final String COL_CHAPTER = "chapter";
public static final String COL_KEYDESCRIPTOR = "keydescriptor";
public static final String COL_RULETITLE = "ruletitle";
public static final String COL_DESCR = "descr";
public static final String COL_DESCRIPTION = "description"; // LongText
public static final String COL_LABEL = "label";
public static final String COL_USERLABEL = "userlabel";
public static final String COL_LABELID = "labelid"; // Integer
public static final String COL_USERLABELID = "userlabelid"; // Integer
public static final String COL_TDTIP = "tdtip";
public static final String COL_DEFUNCT = "defunct";
public static final String COL_USCFREVISION = "uscfrevision";
public static final String COL_HIGHLIGHT = "highlight"; // LongText
public static final String COL_PAGENO = "pageno";
public static final String COL_CHANGEDATE = "changedate"; // Integer
public static final String COL_REFINC = "refinc";
public static final String COL_RULEINC = "ruleinc";
// keydescriptors table columns
public static final String COL_KEYDESC = "keydesc";
public static final String COL_IMAGEREFERENCE = "imagereference";
// indexheader table columns
public static final String COL_INDEX_HEADER = "index_header";
public static final String COL_INDEXID = "indexid";
public static final String COL_INDEXLEXICON = "indexlexicon";
// columns defined in RAW Queries via AS
public static final String COLRAW_XREFRULE = "xrefrule";
public static final String COL_PROJ_THECOUNT = "count(*) as thecound";
public static final String COLTHECOUNT = "thecound";
public static final String[] PROJECTION_CHAPTER = { KEY_ROWID, COL_CHAPTER,
COL_CHAPTERTITLE, };
public static final String[] PROJECTION_RULES = { KEY_ROWID, COL_CHAPTER,
COL_RULE, COL_RULETITLE, COL_KEYDESCRIPTOR, };
/* ************** MAKE SURE THERE'S and _id *********************** */
public static final String RAWQUERY_RULESDESCRIBED = "select rules._id, rule, chapter, ruletitle, rules.keydescriptor, keydesc FROM rules LEFT OUTER JOIN keydescriptors ON rules.keydescriptor = keydescriptors.keydescriptor WHERE chapter = ? group by 1, 2, 3, 4, 5 order by 1, 4";
public static final String RAWQUERY_RULESCROSSREF = "SELECT rules._id, rules.rule, rules.ruletitle, rulereferences.rule AS xrefrule FROM rulereferences INNER JOIN rules ON rulereferences.rulereference = rules.rule WHERE (((rules.keydescriptor)='2') AND ((rulereferences.rule)= ? )) ORDER BY rules.rule";
public static final String RAWQUERY_RULESCROSSREFDETAIL = "SELECT rules._id, rules.rule, rules.ruletitle, keydescriptors.keydesc FROM rules INNER JOIN keydescriptors ON rules.keydescriptor = keydescriptors.keydescriptor WHERE (((rules.rule)= ? )) GROUP BY rules.rule, rules.ruletitle, keydescriptors.keydesc ORDER BY rules.rule, Min(rules.keydescriptor)";
public static final String RAWQUERY_INDEX = "SELECT DISTINCT _id, indexlexicon FROM indexheader GROUP BY 2 ORDER BY 2";
public static final String RAWQUERY_INDEXCHILD = "SELECT indexheader._id, indexheader.index_header, indexer.rule, indexheader.indexid, rules.pageno, rules.uscfrevision, rules.defunct FROM (indexheader LEFT JOIN indexer ON indexheader.indexid = indexer.indexid) LEFT JOIN rules ON indexer.rule = rules.rule WHERE (((indexheader.indexlexicon)= ? )) GROUP BY indexheader._id, indexheader.index_header, indexer.rule, indexheader.indexid, rules.pageno, rules.uscfrevision, rules.defunct HAVING (((rules.pageno) Is Not Null)) ORDER BY indexheader.index_header, indexheader.indexid";
public static final String RAWQUERY_RULEDETAIL = "SELECT rules.rule, rules.ruletitle, rules.descr, rules.description, rules.keydescriptor, rules.tdtip, rules.uscfrevision, rules.defunct, rules.highlight, rules.label, rules.userlabel, rules.changedate FROM rules GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 HAVING (((rules.rule)= ? )) ORDER BY 1, 5";
public static final String RAWQUERY_LABEL = "select distinct _id, label, count(*) as thecount from rules group by 2 order by label";
public static final String RAWQUERY_LABELDETAIL = "SELECT rules._id, rules.rule, rules.ruletitle, keydescriptors.keydesc FROM rules INNER JOIN keydescriptors ON rules.keydescriptor = keydescriptors.keydescriptor WHERE (((rules.label)= ? )) GROUP BY rules.rule, rules.ruletitle, keydescriptors.keydesc ORDER BY rules.rule, Min(rules.keydescriptor)";
public static void onCreate(SQLiteDatabase db) {
Log.i(LOG_TAG, "onCreate");
}
public static void onUpgrade(SQLiteDatabase db, int oldVersion,
int newVersion) {
Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
onCreate(db);
}
}
And here is the database helper (TDAdbHelper.java):
package com.birdsall.tda;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class TDAdbHelper extends SQLiteOpenHelper {
public static String DATABASE_PATH;
public static final String DATABASE_NAME = "tda.db";
private static final int DATABASE_VERSION = 1;
private Context context;
private SQLiteDatabase db;
public TDAdbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
String packageName = context.getPackageName();
DATABASE_PATH = String.format("//data//data//%s//databases//",
packageName);
Log.i(this.getClass().toString(), "... before calling openDatabase ");
openDataBase();
Log.i(this.getClass().toString(), "... after return openDatabase ");
}
@Override
public void onCreate(SQLiteDatabase db) {
Log.i(this.getClass().toString(), "... Starting TDAdb.onCreate ");
TDAdb.onCreate(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
TDAdb.onUpgrade(db, oldVersion, newVersion);
}
// Performing a database existence check
private boolean checkDataBase() {
Log.i(this.getClass().toString(), "... Starting checkDatabase ");
SQLiteDatabase checkDb = null;
try {
String path = DATABASE_PATH + DATABASE_NAME;
checkDb = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
Log.e(this.getClass().toString(), "Error while checking db");
}
// Android doesn’t like resource leaks, everything should
// be closed
if (checkDb != null) {
checkDb.close();
}
return checkDb != null;
}
// Method for copying the database
private void copyDataBase() throws IOException {
// Open a stream for reading from our ready-made database
// The stream source is located in the assets
Log.i(this.getClass().toString(), "... in copyDataBase ");
InputStream externalDbStream = context.getAssets().open(DATABASE_NAME);
// Path to the created empty database on your Android device
String outFileName = DATABASE_PATH + DATABASE_NAME;
// Now create a stream for writing the database byte by byte
OutputStream localDbStream = new FileOutputStream(outFileName);
// Copying the database
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = externalDbStream.read(buffer)) > 0) {
localDbStream.write(buffer, 0, bytesRead);
}
// Don’t forget to close the streams
localDbStream.close();
externalDbStream.close();
}
// This piece of code will create a database if it’s not yet created
public void createDataBase() {
Log.i(this.getClass().toString(), "... in createDataBase ");
boolean dbExist = checkDataBase();
if (!dbExist) {
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
Log.e(this.getClass().toString(), "Copying error");
throw new Error("Error copying database!");
}
} else {
Log.i(this.getClass().toString(), "Database already exists");
}
}
public SQLiteDatabase openDataBase() throws SQLException {
String path = DATABASE_PATH + DATABASE_NAME;
Log.i(this.getClass().toString(), "Starting openDatabase " + path);
if (db == null) {
createDataBase();
db = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.OPEN_READWRITE);
}
return db;
}
}
For completeness there is the main activity (ActivityMain.java), it's as simple a Hello World:
package com.birdsall.tda;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class ActivityMain extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
And the simple layout for the main activity (activity_main.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ActivityMain"
>
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="64dp"
android:layout_marginTop="22dp"
android:text="Test Search - using hardware button. Search for 'check'"
android:textAppearance="?android:attr/textAppearanceMedium" />
</RelativeLayout>
So if you have any idea on how to fix or debug this issue, I would appreciate it. Remember I new to Android Development, so a few words as to the why would also be helpful.
Have a great day.
回答1:
I was thinking one thing and not doing another. The suggestions show up, clicking on one of them OF COURSE will not show anything, there's no implemented method to handle the onClick in the SearchAdapter (or whatever it's called). When you hit the 'Go' button on the keyboard, then the results are posted to the listview. Obviously I was too close to the problem, me.
来源:https://stackoverflow.com/questions/17282800/android-search-not-returning-results-after-suggestion-chosen