ChangeCursor throws a CalledFromWrongThreadException exception ... but only once

I work with a list and context menu. When the user long presses an item in the context menu, a small note appears next to the item in the list so that the user knows that the item is marked as favorites. All this works well, EXCEPT that it forces the force to close the first time it is done. Every time everything works great. 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): E/AndroidRuntime (5362): FATAL : E/AndroidRuntime (5362): android.view.ViewRoot $CalledFromWrongThreadException: , . E/AndroidRuntime (5362): android.view.ViewRoot.checkThread(ViewRoot.java:2932) E/AndroidRuntime (5362): android.view.ViewRoot.requestLayout(ViewRoot.java:629) E/AndroidRuntime (5362): android.view.View.requestLayout(View.java:8267) E/AndroidRuntime (5362): android.view.View.requestLayout(View.java:8267) E/AndroidRuntime (5362): android.view.View.requestLayout(View.java:8267) E/AndroidRuntime (5362): android.view.View.requestLayout(View.java:8267) E/AndroidRuntime (5362): android.widget.AbsListView.requestLayout(AbsListView.java:1102) E/AndroidRuntime (5362): android.widget.AdapterView $AdapterDataSetObserver.onChanged(AdapterView.java:790) E/AndroidRuntime (5362): android.database.DataSetObservable.notifyChanged(DataSetObservable.java:31) E/AndroidRuntime (5362): android.widget.BaseAdapter.notifyDataSetChanged(BaseAdapter.java:50) E/AndroidRuntime (5362): android.widget.CursorAdapter.changeCursor(CursorAdapter.java:260) E/AndroidRuntime (5362): com.zalzala.spellbookpf.SpellsAz $bgRequery.onPostExecute(SpellsAz.java:228) E/AndroidRuntime (5362): com.zalzala.spellbookpf.SpellsAz $bgRequery.onPostExecute(SpellsAz.java:1) E/AndroidRuntime (5362): android.os.AsyncTask.finish(AsyncTask.java:417)

, , spellsAdapter.changeCursor(mSpellCursor); onPostExecute. ui, , . , , , . , , , . - .

, - , :

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();
}
}

.

EDIT: , , , ui, .

+3
3

. onContentChanged CursorAdapter. :

private class NearbyLocationListAdapter extends CursorAdapter
{
    private static final String TAG = "NearbyLocationListAdapter";

    private NearbyLocationActivity mActivity = null;
    private Thread mContentChanageHanderThread = null;

    public NearbyLocationListAdapter(NearbyLocationActivity activity, Cursor c) {
        super(activity, c, false);
        this.mActivity = activity;
    }

    // called when adding location info into location db
    @Override
    protected void onContentChanged() {
        // AsyncTask will bring RejectedExecutionException, change to use Thread
        // interrupt the original thread since data is updated again
        clearContentChanageHanderThread();

        mContentChanageHanderThread = new Thread(new Runnable() {
            // set as synchronized in case called by multiple thread
            @Override
            public synchronized void run() {
                if (Thread.currentThread().isInterrupted()) {
                    // Log.d(TAG, "Thread interrupted. Aborting.");
                    return;
                }

                final Cursor cursor = getLocationCursor();

                if (Thread.currentThread().isInterrupted()) {
                    // Log.d(TAG, "Thread interrupted after getting cursor. Aborting.");
                    if (Util.isValid(cursor)) {
                        cursor.close();
                    }

                    return;
                }

                NearbyLocationActivity.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        if (Util.isValid(cursor)) {
                            // will close the original cursor and switch to new one
                            // notifyDataSetChanged will be called inside
                            // Log.d(TAG, "onContentChanged: will changeCursor");
                            changeCursor(cursor);
                        }
                        else {
                            Log.e(TAG, "onContentChanged: cursor is null or closed");
                        }
                    }
                });

            }
        });

        mContentChanageHanderThread.start();

    }

.....................................
public void clearContentChanageHanderThread() {
        //Log.v(TAG,"clearContentChanageHanderThread");

        if (mContentChanageHanderThread != null && mContentChanageHanderThread.isAlive()) {
            mContentChanageHanderThread.interrupt();
            mContentChanageHanderThread = null;
        }
    }

}
0

runOnUiThread? :

runOnUiThread(new Runnable() {
    public void run() {
        spellsAdapter.changeCursor(mSpellCursor);
    }
});

, , , .

0

You must use the Handler to manage user interface objects. You cannot (and cannot) use or touch spellsAdapterin AsyncTask without using a handler with runnable.

At first I was horrified to understand such problems and deal with them. But not so much when I think of it as two roads at different speeds. You need a bridge and control to drive cars;) information about each other. This is why Handler exists

0
source

All Articles