0

내 어댑터를 업데이트하기 ASyncTask를 호출하고있어, 한 번 한 동안 나는 내 목록java.lang.IllegalStateException 오류

E/AndroidRuntime (16197)를 채우는 것이 런타임 오류를 받고 있어요 : java.lang.IllegalStateException : 어댑터의 내용이 변경되었지만 ListView가 알림을받지 못했습니다. 어댑터의 내용이 백그라운드 스레드에서 수정되지 않고 UI 스레드에서만 수정되었는지 확인하십시오.

오류 메시지에서 암시하는 것처럼 내 어댑터를 백그라운드 스레드에서 채우는 것은 좋지 않은 생각입니까?

나는 onPostExecute()에서 notifyDataSetChanged()을 호출하지만, onProgressUpdate()에서 전화 할 때도 같은 오류가 발생합니다.

여기 내 ASyncTask입니다. fetchOneItem()은 항목 목록에서 단일 항목을 가져오고 이름/설명을 채우고 다음 반복에서 다음 항목으로 이동한다고 가정합니다.

private class GetItems extends AsyncTask<Integer, Integer, Integer> { 
    @Override 
    protected void onPreExecute() { 
    super.onPreExecute(); 
    pd = new ProgressDialog(context); 
    pd.setTitle("Displaying..."); 
    pd.setMessage("Please wait."); 
    pd.setCancelable(false); 
    pd.setIndeterminate(true); 
    pd.show(); 
    TextView myMsg = (TextView)findViewById(R.id.topMsg); 
    myMsg.setText("Displaying items..."); 
    } 

    protected Integer doInBackground(Integer... params) { 
    // TODO Auto-generated method stub 
    do { 
     fetchOneItem(); // fetches 1 item 
     myList.add(new Item(name, description)); 
     Log.i(TAG, "Adding " + name + ", " + desc + ", adapter size = " + myAdapter.getCount()); 
     publishProgress(i); 
    } while (doneFetching != true); 
    Log.i(TAG, "Completed loading, i =" + i); 
    return i; 
    } 

    protected void onProgressUpdate(Integer... values) { 
    TextView myMsg = (TextView)findViewById(R.id.topMsg); 
    myMsg.setText("Displaying " + Integer.toString(values[0].intValue()) + " items"); 
    } 

    @Override 
    protected void onPostExecute(Integer numItems) { 
    TextView myMsg = (TextView)findViewById(R.id.topMsg); 
    myMsg.setText("Displayed " + numItems + " items"); 
    myAdapter.notifyDataSetChanged(); 
    allItemsLoaded = true; 
    Log.i(TAG, "Adapter size = " + myAdapter.getCount()); 
    pd.dismiss(); 
    } 
} 
+0

백그라운드 스레드에서 직접 '어댑터'를 업데이트하는 대신 (중간) 결과를 UI 스레드로 보내고 그 결과를 어댑터에 추가하십시오. 두 함수 모두 UI 스레드에서 호출되기 때문에'onProgressUpdate()'또는'onPostExecute()'가 수행합니다. –

+0

원본 게시물에 내 AsyncTask 코드를 추가했습니다. Adapter를 업데이트함으로써 myList.add()를 doInBackground()에서 onProgressUpdate() 또는 onPostExecute()로 이동한다는 의미입니까? 그렇다면 두 개의 변수 (정수 및 항목)를 두 메서드에 전달하는 방법은 무엇입니까? – user1118764

+0

네, 맞습니다. 메서드에 여러 값을 전달하려면 간단한 객체로 묶어야합니다 (예 :'Tuple '). 또는 AsyncTask 기간 동안 임시 목록을 할당하고 백그라운드 스레드의 항목을 추가 한 다음 UI 스레드의 어댑터로 가져 와서 추가 할 수 있습니다. 이는 생산자 - 소비자 패턴과 유사 할 수 있지만 콜백이 동시에 발생하지 않으므로 훨씬 간단합니다. –

답변

0

네가 볼 수 있듯이 안드로이드는 이것을 허용하지 않습니다. 보기를 업데이트하려면 UI 스레드에서 수행해야합니다. 그러나 안드로이드에서는 허용되지 않는 네트워크 통신과 같은 UI 스레드에서 느린 작업을 절대로 사용하지 않아야합니다. 실제로 UI 코드를 가능한 짧게 유지해야합니다. 너무 오래 걸리면 Android에서 앱이 다운 된 것으로 생각할 수 있습니다.

편집 : 당신이 업데이트 할 때 변경 사항을 알리는 ArrayAdapter를 사용하고 있다고 생각합니다.

이 ArrayAdapter와를 사용하지만, UI 스레드에서 업데이트 :

그래서 당신은 두 가지 선택을 얻었다.

또는 BaseAdapter의 고유 한 하위 클래스를 구현하면 올바르게 수행 할 수 있습니다.

ArrayAdapter는 정적 데이터 세트가있는 간단한 상황을위한 것입니다.

+0

이것은 다소 잡동사니 22 상황입니다. 보기를 업데이트하고 싶습니다. 그래서 UI 스레드에서 수행해야합니다. 느린 작업이므로 UI ​​스레드에서 절대 수행해서는 안됩니다. 정확히 어떻게해야합니까? – user1118764

+0

글쎄 그냥 오래된 코드를 보았고 apperently onPostExecute() UI에서 실행해야합니다. 내가 너를 올바르게 이해한다면 네가 올바르게하고있다. 코드 샘플을 보여줄 수 있습니까? – wtfmoments

+0

원본 게시물에 내 AsyncTask 코드를 추가했습니다. 내가 도대체 ​​뭘 잘못하고있는 겁니까? – user1118764

0

"실시간"으로 목록보기를 업데이트하려고합니다.

글쎄, 어떻게해야할지 모르겠다. 그래서이 AsyncTask를 호출하는 새로 고침 버튼을 구현하는 것이 좋다. 실제로 목록보기를 업데이트하고 있음을 사용자에게 표시하려면 DialogProgress를 보내는 것이 중요합니다. 희망이 도움이됩니다!

+0

나는 사용자에게 뭔가 일어나고 있음을 알리는 progressdialog가 있습니다. AsyncTask가 실행되고 목록 항목을 자동으로 가져 오므로 사용자가 새로 고침 버튼을 눌러서 개입해야하는 것은 원하지 않습니다. 자동으로 수행해야합니다. – user1118764

0

게시는 늦지 만 비슷한 상황에 직면하는 사람을 도울 수 있습니다.

문제점 : 사용자가 장기간 실행 중이며 백그라운드 스레드로 가져 가고 싶습니다. 또한 작업이 완료되면 UI 스레드에 대한보기를 업데이트하려고합니다.

솔루션 :

//spawn a new thread for background processing. 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
      //starting your background operation. 
      //this is a network call. 
      final Location location = BackendApi.builder().getSlaveLocation(mActivity.getApplicationContext(), "xyz", null); 

      //now i want to update views on UI thread. 
      new Handler(Looper.getMainLooper()).post(new Runnable() { 
       @Override 
       public void run() { 
        //this code will run on UI thread. 
        myView.setText("Latitude: " + location.getLatitude()); 
       } 
      }); 
     } 
    }).start(); 

보기/어댑터를 업데이트하기 전에 활동이 파괴되지되어 있는지 확인하십시오.