changeCursor导致CalledFromWrongThreadException异常...但只有一次

时间:2011-06-11 21:35:40

标签: android listview

我正在使用listview和上下文菜单。当用户长时间按下上下文菜单中的项目时,列表中项目旁边会出现一个小标记,让用户知道该项目已被标记为收藏夹。这一切都运作良好,除了它导致力量在第一次完成时关闭。每隔一段时间工作得很好。 以下是相关代码:

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

以下是例外:

  

D / SpellBook(5362):获取光标   E / AndroidRuntime(5362):致命   EXCEPTION:后台线程   E / AndroidRuntime(5362):   android.view.ViewRoot $ CalledFromWrongThreadException:   只创建了原始线程   视图层次结构可以触及其视图。   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)

所以当spellsAdapter.changeCursor(mSpellCursor)时会发生错误;在onPostExecute中调用。由于该函数在ui线程中运行,我很难理解为什么我得到后台Tread错误。让这更难理解和调试的是,它真的只发生在我第一次这样做的时候。每隔一段时间它工作正常,即使应用程序刚刚启动或手机重新启动。重现该错误的唯一方法是卸载该应用程序并重新安装它。

万一有人需要它,我包括我的适配器的代码:

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

感谢您的帮助。

修改 我可以通过不在单独的线程中调用游标来解决这个问题,但我认为使用另一个线程而不是使用ui线程是一个好习惯,所以我仍然会喜欢一些帮助来解决这个问题。

3 个答案:

答案 0 :(得分:0)

您是否尝试过使用runOnUiThread方法?基本上它看起来像这样:

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

这样你可以在一个单独的线程中进行任何长时间的计算,但是当需要更新UI时,可以在正确的线程中完成。

答案 1 :(得分:0)

您必须使用处理程序才能管理UI对象。 如果不使用带有可运行的处理程序,则不能(并且不能)使用或触摸spellsAdapter到AsyncTask中。

起初我很难理解这类问题并处理它们。但是,当我把它想象成两条不同速度的道路时,并没有那么多。你需要一些桥梁和控制来管理汽车;)相互信息。这就是Handler存在的原因

答案 2 :(得分:0)

异步任务有时可能会溢出。你最好在CursorAdapter的onContentChanged中使用自己的线程。这是我的代码供您参考:

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

}