2011-06-11 8 views
3

나는 listview 및 컨텍스트 메뉴로 작업하고 있습니다. 사용자가 컨텍스트 메뉴에서 항목을 길게 누르면 목록에 항목 옆에 작은 기호가 나타나 항목이 즐겨 찾기로 표시되었음을 알립니다. 이 작업이 처음 수행 될 때 강제 종료가 발생하는 것을 제외하고는 모두 잘 작동합니다. 다른 모든 시간은 잘 작동합니다. (5362)changeCursor는 CalledFromWrongThreadException 예외를 발생시킵니다. 단 한 번만

D/마법서 : 여기서

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

예외이다 여기 관련 코드 커서 페치 E/AndroidRuntime (5362) FATAL EXCEPTION : 배경 스레드 E/AndroidRuntime (5362) : android.view.ViewRoot $ CalledFromWrongThreadException : 뷰 계층을 으로 만든 원래 스레드 만 해당 뷰를 터치 할 수 있습니다. E/AndroidRuntime (5362) android.view.ViewRoot.checkThread (ViewRoot.java:2932) (5362) E/AndroidRuntime에서 : android.view.ViewRoot.requestLayout (ViewRoot.java:629)에서 E /AndroidRuntime (5362) android.view.View.requestLayout (View.java:8267)에서 E/AndroidRuntime : android.view.View.requestLayout (View.java:8267) (5362) E/AndroidRuntime에서 (5362) : android.view.View.requestLayout (View.java:8267) E/AndroidRuntime (5362) : 에서 android.view.View.requestLayout (View.java:8267) E/AndroidRuntime (5362)) : at android.widget.AbsListView.requestLayout (AbsListView.ja) 버지니아 : 1102) E/AndroidRuntime (5362) : android.widget.AdapterView $ AdapterDataSetObserver.onChanged (AdapterView.java:790) (5362) E/AndroidRuntime에서 : android.database.DataSetObservable.notifyChanged (DataSetObservable에서. 자바 31) E/AndroidRuntime (5362)에서 android.widget.CursorAdapter.changeCursor (CursorAdapter.java : android.widget.BaseAdapter.notifyDataSetChanged (BaseAdapter.java:50) (5362) E/AndroidRuntime에서 : 260) E/AndroidRuntime (5362) : com.zalzala.spellbookpf.SpellsAz $ bgRequery.onPostExecute (SpellsAz.java:228) (5362) E/AndroidRuntime에서 : 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(); 
} 
} 

감사 : 경우 사람이, 내 어댑터의 코드를 포함하고있어 그것을 필요로 그냥에서

.

편집 : 내가 별도의 스레드에서 커서를 호출하지 않음으로써이 문제를 해결할 수 있습니다,하지만 난 그것을 위해 다른 스레드를 사용하는 대신 UI 스레드를 사용하는 것이 좋다라고 생각, 그래서 난 아직도 사랑 일부는 이것을 알아내는 데 도움이됩니다.

+0

처음으로 어떻게 목록을 채우는 지 비정상적인 점은 무엇입니까? – CommonsWare

+0

나는 아무것도 생각할 수 없다. 매번 커서를 똑같은 방식으로 호출합니다. – anakin78z

답변

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

} 
0

runOnUiThread 메소드를 사용해 보셨습니까? 기본적으로는 다음과 같이 보일 것이다 :

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

는 UI를 업데이트 할 시간을 때, 그 올바른 스레드에서 수행하는 별도의 스레드에서 긴 어떤 계산하지만, 당신이 할 수있는 그 방법을.

0

UI 개체를 사용하여 관리하려면 처리기을 사용해야합니다. runnable이있는 핸들러를 사용하지 않고 spellsAdapter을 AsyncTask에 사용하거나 만질 수는 없습니다.

처음에는 이런 유형의 문제를 이해하고 처리하는 것이 끔찍했습니다. 그러나 내가 그렇게 생각할 때 다른 속도와 함께 2 개의 길처럼 생각하지 마라. 당신은 차를 관리하기 위해 약간의 다리와 통제가 필요하다;) 정보 서로. 그것이 처리기가 존재하는 이유입니다.