I’m working with a listview and a context menu. When the user long presses an item in the context menu, a little mark appears next to the item in the list, to let the user know the item has been marked a favorite. This is all working well, EXCEPT that it causes a force close the very first time this is done. Every other time works just fine.
Here is the relevant code:
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
switch (item.getItemId()) {
case 0:
mDbHelper.updateFavorite(info.id, 1);
new bgRequery().execute();
return true;
case 1:
mDbHelper.updateFavorite(info.id, 0);
new bgRequery().execute();
return true;
default:
return super.onContextItemSelected(item);
}
}
private class bgRequery extends AsyncTask<Void, Integer, Void> {
@Override
protected Void doInBackground(Void... voids ) {
mSpellCursor = fetchCursor();
return null;
}
@Override
protected void onPostExecute(Void voids) {
spellsAdapter.changeCursor(mSpellCursor);
}
}
Here is the exception:
D/SpellBook( 5362): fetching Cursor
E/AndroidRuntime( 5362): FATAL
EXCEPTION: background thread
E/AndroidRuntime( 5362):
android.view.ViewRoot$CalledFromWrongThreadException:
Only the original thread that created
a view hierarchy can touch its views.
E/AndroidRuntime( 5362): at
android.view.ViewRoot.checkThread(ViewRoot.java:2932)
E/AndroidRuntime( 5362): at
android.view.ViewRoot.requestLayout(ViewRoot.java:629)
E/AndroidRuntime( 5362): at
android.view.View.requestLayout(View.java:8267)
E/AndroidRuntime( 5362): at
android.view.View.requestLayout(View.java:8267)
E/AndroidRuntime( 5362): at
android.view.View.requestLayout(View.java:8267)
E/AndroidRuntime( 5362): at
android.view.View.requestLayout(View.java:8267)
E/AndroidRuntime( 5362): at
android.widget.AbsListView.requestLayout(AbsListView.java:1102)
E/AndroidRuntime( 5362): at
android.widget.AdapterView$AdapterDataSetObserver.onChanged(AdapterView.java:790)
E/AndroidRuntime( 5362): at
android.database.DataSetObservable.notifyChanged(DataSetObservable.java:31)
E/AndroidRuntime( 5362): at
android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50)
E/AndroidRuntime( 5362): at
android.widget.CursorAdapter.changeCursor(CursorAdapter.java:260)
E/AndroidRuntime( 5362): at
com.zalzala.spellbookpf.SpellsAz$bgRequery.onPostExecute(SpellsAz.java:228)
E/AndroidRuntime( 5362): at
com.zalzala.spellbookpf.SpellsAz$bgRequery.onPostExecute(SpellsAz.java:1)
E/AndroidRuntime( 5362): at
android.os.AsyncTask.finish(AsyncTask.java:417)
So the error occurs when spellsAdapter.changeCursor(mSpellCursor); is called in onPostExecute. Since that function runs in the ui thread, I’m having a hard time understanding why I’m getting a background Tread error. What’s making this harder to understand and debug is that it really only happens the very first time I do this. Every other time it works fine, even when the app starts fresh or the phone has rebooted. The only way to reproduce the bug is to uninstall the app and install it fresh.
Just in case anyone needs it, I’m including the code of my adapter:
public class SpellListAdapter extends CursorAdapter {
private LayoutInflater mLayoutInflater;
private Context mContext;
public SpellListAdapter(Context context, Cursor c) {
super(context, c);
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v = mLayoutInflater.inflate(R.layout.list_item_fave, parent, false);
return v;
}
@Override
public void bindView(View v, Context context, Cursor c) {
String spell = c.getString(c.getColumnIndexOrThrow(SpellDbAdapter.KEY_SPELL));
int fave = c.getInt(c.getColumnIndexOrThrow(SpellDbAdapter.KEY_FAVORITE));
/**
* Next set the title of the entry.
*/
TextView Spell = (TextView) v.findViewById(R.id.text);
if (Spell != null) {
Spell.setText(spell);
}
//Set Fave Icon
TextView Fave = (TextView) v.findViewById(R.id.fave_icon);
Fave.setVisibility(View.INVISIBLE);
if (fave == 1){
Fave.setVisibility(View.VISIBLE);
}
}
public void update() {
notifyDataSetChanged();
}
}
Thanks for your help.
EDIT:
I can work around this issue by not calling the cursor in a separate thread, but I thought it was good practice to use another thread for it instead of using the ui thread, so I would still love some help figuring this out.
The async task may overflow sometimes. You’d better use you own thread in the onContentChanged of your CursorAdapter. Here is my code for your reference: