Android、ListView IllegalStateException:「アダプターのコンテンツは変更されましたが、ListViewは通知を受け取りませんでした」


188

私がやりたいこと:結果が計算されている間に、ListViewコンテンツを計算し、ListViewを部分的に更新するバックグラウンドスレッドを実行します。

私が回避しなければならないことを知っています:バックグラウンドスレッドからListAdapterの内容をいじることはできないので、onProgressUpdateからAsyncTaskを継承し、結果をパブリッシュ(アダプターにエントリを追加)します。私のアダプターは結果オブジェクトのArrayListを使用し、それらのarraylistのすべての操作が同期されます。

他の人の研究ここには非常に貴重なデータがあります。また、約500人のユーザーのグループでほぼ毎日クラッシュすることもありlist.setVisibility(GONE)/trackList.setVisibility(VISIBLE)、onProgressUpdateにブロックを追加すると、クラッシュが10分の1に低下しましたが、消えることはありませんでした。(回答で提案されまし

私が時々得たもの:気づいてください、それは本当にまれにしか起こりません(3.5kユーザーの1人に1週間に1回)。しかし、私はこのバグを完全に取り除きたいと思います。ここに部分的なスタックトレースがあります:

`java.lang.IllegalStateException:` The content of the adapter has changed but ListView  did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter(class com.transportoid.Tracks.TrackListAdapter)]
at android.widget.ListView.layoutChildren(ListView.java:1432)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:2062)
at android.widget.ListView.onTouchEvent(ListView.java:3234)
at android.view.View.dispatchTouchEvent(View.java:3709)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:852)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:884)
[...]

助けて?もう必要ありません。以下を参照してください

notifyDataSetChanged最終的な回答:結局のところ、ちらつきや突然のリストの変更を回避するために、5回の挿入ごとに呼び出していました。そのように行うことはできません。ベースリストが変更された場合は、常にアダプタに通知してください。このバグは、長い間私にはなくなっていました。


3
notifyDataSetChanged()を呼び出しましたか?
Denis Palnitsky

1
- addObjectToList /リスト/上で操作を同期- notifyDataSetChanged() - list.setVisibility(VISIBLE)(および可視性の修正なし例外がはるかに頻繁に発生する)list.setVisibility(GONE):もちろん、onProgressUpdateにシーケンスそこに行く
tomash

1
別のスレッドで基になるArrayListを変更していますか?アダプターによって参照されるArrayListへのすべての変更は、UIスレッドで発生する必要があります。
リッチシュラー2010年

2
@Qberticus-私が明確に述べたように、別のスレッドからではなく、AcyncTaskのメソッドonProgressUpdateからArrayListを変更します-GUIスレッドで動作します。
2010年

1
@tomash私は同じ例外を取得しました。私はプルを使用して更新し、実行後はadapter.notifyDataSetChanged();を記述しました; lvAutherlist.completeRefreshing(); しかし、時々このエラーを解決する方法を得ました
Khan

回答:


119

同じ問題がありました。

ArrayListUIスレッドの外側にアイテムを追加していました。

解決策:両方を実行し、UIスレッドでadding the items呼び出しnotifyDataSetChanged()ました。


4
UIスレッドでアイテムを追加してnotifyDataSetChanged()を呼び出しましたが、これを解決しました。
マリンズ

23
本当に良いコメントです。
dentex

2
これはマルチスレッドの問題であり、適切に同期されたブロックの使用これは回避できます。UIスレッドに余分なものを入れたり、アプリの応答性を低下させたりせずに、以下の私の答えを確認してください。
Javanator 2014年

2
将来の読者のために-バックグラウンドスレッドで計算を行うのは良いことですが、UIスレッドに結果を渡して、同じコードブロックでアダプターのベースコレクションにアイテムを追加し、アダプターに通知する必要があります。
2014

4
?@Mullins uは、私は同じ問題にこだわっているソリューションのためのサンプルを表示してくださいすることができます
ジャス

27

同じ問題がありましたが、方法を使用して修正しました

requestLayout();

クラスから ListView


35
いつこのメソッドを呼び出す必要がありますか?
JehandadK 2013

3
@DeBuGGeRは、ListViewに項目を追加するか、そのアダプタを変更した後。
アフメットノヤンクズルタン2013年

asynctaskに要素を追加している場合
aNdRO博士

2
記録のために、@ Dr.aNdROの場合、AsyncTaskで結果を収集します。doInBackground()を実行して結果を保存し、UIスレッドで実行されるAsyncTask.onPostExecute()のリストを更新します。更新する必要がある場合は、UIスレッドでも実行されるAsyncTask.publishProgress()およびAsyncTask.onProgressUpdate()を使用してください。
Nicole Borrelli、2014

21

これはマルチスレッドの問題であり、適切に同期されたブロックの使用これは回避できます。UIスレッドに余分なものを入れたり、アプリの応答性を損なったりすることなく。

私も同じように直面しました。そして、最も受け入れられている回答が示唆するように、UIスレッドからアダプターデータを変更することで問題を解決できます。これは機能しますが、すばやく簡単な解決策ですが、最善の解決策ではありません。

通常のケースでわかるように。バックグラウンドスレッドからのデータアダプターの更新とUIスレッドでのnotifyDataSetChangedの呼び出しは機能します。

このillegalStateExceptionは、UIスレッドがビューを更新していて、別のバックグラウンドスレッドがデータを再び変更したときに発生します。その瞬間がこの問題の原因です。

したがって、アダプタデータを変更してnotifydatasetchange呼び出しを行うすべてのコードを同期する場合。この問題はなくなるはずです。私のために行ったように、私はまだバックグラウンドスレッドからデータを更新しています。

これは、他の人が参照するためのケース固有のコードです。

メイン画面のローダーは、電話帳の連絡先をバックグラウンドでデータソースに読み込みます。

    @Override
    public Void loadInBackground() {
        Log.v(TAG, "Init loadings contacts");
        synchronized (SingleTonProvider.getInstance()) {
            PhoneBookManager.preparePhoneBookContacts(getContext());
        }
    }

このPhoneBookManager.getPhoneBookContactsは、電話帳から連絡先を読み取り、ハッシュマップに入力します。これは、リストアダプターがリストを描画するために直接使用できます。

画面にボタンがあります。これにより、これらの電話番号がリストされるアクティビティが開きます。前のスレッドがその作業を完了する前にリストに直接setAdapterを設定すると、高速なナビゲーションのケースが発生する頻度が低くなります。それは例外をポップアップします。これはこのSO質問のタイトルです。だから私は2番目の活動でこのようなことをしなければなりません。

2番目のアクティビティのローダーは、最初のスレッドが完了するまで待機します。進行状況バーが表示されるまで。両方のローダーのloadInBackgroundを確認します。

次に、アダプターを作成し、UIスレッドでsetAdapterを呼び出すアクティビティにそれを配信します。

それで私の問題は解決しました。

このコードはスニペットのみです。うまくコンパイルするには、それを変更する必要があります。

@Override
public Loader<PhoneBookContactAdapter> onCreateLoader(int arg0, Bundle arg1) {
    return new PhoneBookContactLoader(this);
}

@Override
public void onLoadFinished(Loader<PhoneBookContactAdapter> arg0, PhoneBookContactAdapter arg1) {
    contactList.setAdapter(adapter = arg1);
}

/*
 * AsyncLoader to load phonebook and notify the list once done.
 */
private static class PhoneBookContactLoader extends AsyncTaskLoader<PhoneBookContactAdapter> {

    private PhoneBookContactAdapter adapter;

    public PhoneBookContactLoader(Context context) {
        super(context);
    }

    @Override
    public PhoneBookContactAdapter loadInBackground() {
        synchronized (SingleTonProvider.getInstance()) {
            return adapter = new PhoneBookContactAdapter(getContext());    
        }
    }

}

お役に立てれば


すみません、どうしましたか。バックグラウンドスレッドコードとnotifydatasetchange呼び出しをどのように同期させましたか?AutocompleteTextViewでデータをフィルタリングするときにdoInBackgroundを使用していて、同じ問題に直面しています...解決するために何かアドバイスをいただけますか?
tonix 2014年

@ user3019105-2つのバックグラウンドスレッドを同期しています。1つはバックグラウンドでデータを更新するもので、もう1つは、UIスレッドに、ハンドラー。ポストメソッドまたは利用可能なrunOnUiThreadメソッドを使用してsetAdadpterまたはnotifydatasetchangedに通知するものです。私の特定のケースでは、シングルトンオブジェクトで同期されたdoInBackgroundを使用して2つのローダーを同期しました。ダッシュボード画面ですぐに使用できるように、スプラッシュ画面自体のデータを準備することから始めます。それが私の必要でした。
Javanator 2014年

この実装のコードのスニペットを投稿していただけませんか?私の場合、アダプターでclear()を呼び出し、フィルターされたオブジェクトの一時的なArrayList <T>を作成してから、addAll(tmpArrayList)を呼び出し、最後にnotifyDataSetChanged()を呼び出しました。このすべての呼び出しは、メインスレッドで実行するpublishResult()Filterメソッド内で行います。これは信頼できるソリューションだと思いますか?
tonix 2014年

@ user3019105編集した回答を確認してください。今、あなたにその有用ホープ
Javanator

この答えは、それが動作する理由で読んでの多くを必要とtutorials.jenkov.com/java-concurrency/synchronized.htmlを開始するには良い場所です
abdu

15

私はこれを2つのリストで解決しました。1つのリストをアダプターのみに使用し、もう1つのリストですべてのデータ変更/更新を行います。これにより、バックグラウンドスレッドの1つのリストを更新してから、メイン/ UIスレッドの「アダプター」リストを更新できます。

List<> data = new ArrayList<>();
List<> adapterData = new ArrayList();

...
adapter = new Adapter(adapterData);
listView.setAdapter(adapter);

// Whenever data needs to be updated, it can be done in a separate thread
void updateDataAsync()
{
    new Thread(new Runnable()
    {
        @Override
        public void run()
        {
            // Make updates the "data" list.
            ...

            // Update your adapter.
            refreshList();
        }
    }).start();
}

void refreshList()
{
    runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            adapterData.clear();
            adapterData.addAll(data);
            adapter.notifyDataSetChanged();
            listView.invalidateViews();
        }
    });
}

7

私はこのコードを記述し、2.1エミュレーターイメージで約12時間実行しましたが、IllegalStateExceptionが発生しませんでした。私はandroidフレームワークにこれに関する疑いの利点を与え、それはおそらくコードのエラーであると言います。これがお役に立てば幸いです。多分あなたはあなたのリストとデータにそれを適応させることができます。

public class ListViewStressTest extends ListActivity {
    ArrayAdapter<String> adapter;
    ListView list;
    AsyncTask<Void, String, Void> task;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        this.adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
        this.list = this.getListView();

        this.list.setAdapter(this.adapter);

        this.task = new AsyncTask<Void, String, Void>() {
            Random r = new Random();
            int[] delete;
            volatile boolean scroll = false;

            @Override
            protected void onProgressUpdate(String... values) {
                if(scroll) {
                    scroll = false;
                    doScroll();
                    return;
                }

                if(values == null) {
                    doDelete();
                    return;
                }

                doUpdate(values);

                if(ListViewStressTest.this.adapter.getCount() > 5000) {
                    ListViewStressTest.this.adapter.clear();
                }
            }

            private void doScroll() {
                if(ListViewStressTest.this.adapter.getCount() == 0) {
                    return;
                }

                int n = r.nextInt(ListViewStressTest.this.adapter.getCount());
                ListViewStressTest.this.list.setSelection(n);
            }

            private void doDelete() {
                int[] d;
                synchronized(this) {
                    d = this.delete;
                }
                if(d == null) {
                    return;
                }
                for(int i = 0 ; i < d.length ; i++) {
                    int index = d[i];
                    if(index >= 0 && index < ListViewStressTest.this.adapter.getCount()) {
                        ListViewStressTest.this.adapter.remove(ListViewStressTest.this.adapter.getItem(index));
                    }
                }
            }

            private void doUpdate(String... values) {
                for(int i = 0 ; i < values.length ; i++) {
                    ListViewStressTest.this.adapter.add(values[i]);
                }
            }

            private void updateList() {
                int number = r.nextInt(30) + 1;
                String[] strings = new String[number];

                for(int i = 0 ; i < number ; i++) {
                    strings[i] = Long.toString(r.nextLong());
                }

                this.publishProgress(strings);
            }

            private void deleteFromList() {
                int number = r.nextInt(20) + 1;
                int[] toDelete = new int[number];

                for(int i = 0 ; i < number ; i++) {
                    int num = ListViewStressTest.this.adapter.getCount();
                    if(num < 2) {
                        break;
                    }
                    toDelete[i] = r.nextInt(num);
                }

                synchronized(this) {
                    this.delete = toDelete;
                }

                this.publishProgress(null);
            }

            private void scrollSomewhere() {
                this.scroll = true;
                this.publishProgress(null);
            }

            @Override
            protected Void doInBackground(Void... params) {
                while(true) {
                    int what = r.nextInt(3);

                    switch(what) {
                        case 0:
                            updateList();
                            break;
                        case 1:
                            deleteFromList();
                            break;
                        case 2:
                            scrollSomewhere();
                            break;
                    }

                    try {
                        Thread.sleep(0);
                    } catch(InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }

        };

        this.task.execute(null);
    }
}

お疲れ様でした!ArrayAdapterメソッドのhasStableIds()= falseに気づきました。行がすべてのnotifyDataSetChanged()の前にソートされたため、私の実装はtrueを返しましたが、これは正しくありませんでした。試してみましょう。クラッシュが解決しない場合は、調査を続けます。宜しくお願いします!
2010年

4

数日前に同じ問題に遭遇し、1日に数千のクラッシュを引き起こしました。約0.1%のユーザーがこの状況を満たしています。私が試したsetVisibility(GONE/VISIBLE)requestLayout()、しかし、クラッシュカウントは少しだけ減少します。

そしてようやく解決しました。とは何もsetVisibility(GONE/VISIBLE)。とは何もrequestLayout()

最後に、更新後のデータHandlerを呼び出すためにaを使用していることが原因であることがわかりましたnotifyDataSetChanged()

  1. データをモデルオブジェクトに更新します(これをDataSourceと呼びます)。
  2. ユーザーがリストビュー(checkForTap()/ onTouchEvent()を呼び出すことがあり、最後にを呼び出すlayoutChildren())に触れる
  3. アダプターはモデルオブジェクトからデータを取得しnotifyDataSetChanged()、ビューを呼び出して更新します

そして、私はgetCount()getItem()およびgetView()で、アダプターにコピーするのではなく、DataSourceのフィールドを直接使用するという別の誤りを犯しました。したがって、最後に次の場合にクラッシュします。

  1. アダプターは、最後の応答が提供するデータを更新します
  2. 次の応答が返されると、DataSourceはデータを更新します。これにより、アイテム数が変更されます
  3. ユーザーがリストビューをタップします。これは、タップ、移動、またはフリップの場合があります。
  4. getCount()そしてgetView()呼び出され、listviewはデータに一貫性がないことを検出し、などの例外をスローしますjava.lang.IllegalStateException: The content of the adapter has changed but...。別の一般的な例外は、でIndexOutOfBoundExceptionヘッダー/フッターを使用する場合ですListView

したがって、ソリューションは簡単notifyDataSetChanged()です。ハンドラーがアダプターをトリガーしてデータを取得し、呼び出すときに、DataSourceからアダプターにデータをコピーするだけです。クラッシュは二度と起こりません。


1
これは私の問題でした。私の場合、getCount()は、notifyDataSetChangedが呼び出される前に利用可能な新しいカウントを返していました。私のリストは「仮想」だったので、更新されたカウントをnotifyDataSetChangedメソッドにキャプチャし、getCount()を介してカウントを要求されたときに、このキャッシュされたカウントを返します。
グレン

3

これが断続的に発生した場合、「もっと読み込む」最後の項目をクリックした後にリストがスクロールされたときにのみこの問題が発生したことがわかりました。リストがスクロールされなかった場合、すべてが正常に機能しました。

多くのデバッグの後、それは私の側のバグでしたが、Androidコードの不整合でもありました。

検証が発生すると、このコードはListViewで実行されます

        } else if (mItemCount != mAdapter.getCount()) {
            throw new IllegalStateException("The content of the adapter has changed but "
                    + "ListView did not receive a notification. Make sure the content of "

しかし、onChangeが発生すると、このコードがAdapterView(ListViewの親)で起動されます。

    @Override
    public void onChanged() {
        mDataChanged = true;
        mOldItemCount = mItemCount;
        mItemCount = getAdapter().getCount();

アダプターが同じであることが保証されていない方法に注意してください!

私の場合、それは 'LoadMoreAdapter'だったので、(基になるオブジェクトにアクセスするために)getAdapter呼び出しでWrappedAdapterを返していました。これにより、余分な「さらに読み込む」項目と例外がスローされるため、カウントが異なります。

私がこれをやったのは、ドキュメントがそれをやってもいいように見えるからです

ListView.getAdapter javadoc

このListViewで現在使用されているアダプターを返します。返されるアダプターは、setAdapter(ListAdapter)に渡されるアダプターと同じではない場合がありますが、WrapperListAdapterである場合があります。


同様の問題が発生しています。それを修正する方法を提案できますか?
May13ank

上記の2つのコードサンプルを見ると、リストビューにmAdapterがあり、リストビュー(AdapterView)の親にgetAdapter()があります。getAdapter()をオーバーライドすると、getAdapter()の結果がmAdapterにならない場合があります。2つのアダプタの数が異なる場合、エラーが発生します。
aaronvargas

3

私の問題は、リストビューと一緒にフィルターを使用することに関連していました。

ListViewの基になるデータモデルを設定または更新するとき、私は次のようなことをしていました:

public void updateUnderlyingContacts(List<Contact> newContacts, String filter)
{
    this.allContacts = newContacts;
    this.filteredContacts = newContacts;
    getFilter().filter(filter);
}

filter()最後の行を呼び出すnotifyDataSetChanged()と、FilterのpublishResults()メソッドで呼び出されるようになります(また、そうする必要があります)。これは、特に高速のNexus 5で特にうまくいく場合がありますが、実際には、遅いデバイスやリソースを大量に消費する状況で気づくであろうバグを隠しています。

問題は、フィルタリングが非同期で行われるため、UIスレッド内のfilter()ステートメントの最後との呼び出しの間にpublishResults()、他のUIスレッドコードが実行され、アダプターのコンテンツが変更される可能性があることです。

実際の修正は簡単です。notifyDataSetChanged()フィルタリングの実行を要求する前に呼び出すだけです。

public void updateUnderlyingContacts(List<Contact> newContacts, String filter)
{
    this.allContacts = newContacts;
    this.filteredContacts = newContacts;
    notifyDataSetChanged(); // Fix
    getFilter().filter(filter);
}

3

Feedオブジェクトのリストがあります。それは追加され、none-UIスレッドから切り捨てられます。以下のアダプターで正常に動作します。FeedAdapter.notifyDataSetChangedとにかく、UIスレッドを呼び出しますが、少し後で呼び出します。UIが停止している場合でもFeedオブジェクトがローカルサービスのメモリに残るため、これが好きです。

public class FeedAdapter extends BaseAdapter {
    private int size = 0;
    private final List<Feed> objects;

    public FeedAdapter(Activity context, List<Feed> objects) {
        this.context = context;
        this.objects = objects;
        size = objects.size();
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ...
    }

    @Override
    public void notifyDataSetChanged() {
        size = objects.size();

        super.notifyDataSetChanged();
    }

    @Override
    public int getCount() {
        return size;
    }

    @Override
    public Object getItem(int position) {
        try {
            return objects.get(position);
        } catch (Error e) {
            return Feed.emptyFeed;
        }
    }

    @Override
    public long getItemId(int position) {
        return position;
    }
}

2

私はまったく同じエラーログで同じ問題に直面していました。私の場合onProgress()、AsyncTaskはを使用して値をアダプターに追加しますmAdapter.add(newEntry)。UIの応答が遅くなるのを避けるために、1 秒に4回設定mAdapter.setNotifyOnChange(false)して呼び出しますmAdapter.notifyDataSetChanged()。1秒に1回、配列がソートされます。

これはうまく機能し、非常に中毒性のあるように見えますが、残念ながら、表示されたリスト項目を何度もタッチするとクラッシュする可能性があります。

しかし、許容できる回避策を見つけたようです。 私が思うに、UIスレッドで作業しているだけでも、アダプターはを呼び出さずにデータの多くの変更を受け入れないnotifyDataSetChanged()ため、前述の300ミリ秒が経過するまですべての新しいアイテムを格納するキューを作成しました。この瞬間に達したら、保存されているすべてのアイテムを一度に追加して、を呼び出しますnotifyDataSetChanged()。今までは、リストをクラッシュさせることはできませんでした。



2

XMPP通知アプリケーションで同じ問題に直面しても、受信者メッセージをリストビューに追加する必要があります(で実装ArrayList)。MessageListener(別のスレッド)を使用してレシーバーのコンテンツを追加しようとすると、アプリケーションが上記のエラーで終了します。これを解決するには、Activityクラスの一部であるarraylistsetListviewadapaterthrough runOnUiThreadメソッドにコンテンツを追加します。これは私の問題を解決しました。


1

私は同様の問題に直面しました、ここで私が私の場合に解決した方法です。task既にあるRUNNINGかどうか、またはFINISHEDタスクが1回しか実行できないために確認します。以下に、私のソリューションからの部分的で適応されたコードを示します。

public class MyActivity... {
    private MyTask task;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
       // your code
       task = new MyTask();
       setList();
    }

    private void setList() {
    if (task != null)
        if (task.getStatus().equals(AsyncTask.Status.RUNNING)){
            task.cancel(true);
            task = new MyTask();
            task.execute();         
        } else if (task.getStatus().equals(AsyncTask.Status.FINISHED)) {
            task = new MyTask();
            task.execute();
        } else 
            task.execute();
    }

    class MyTask extends AsyncTask<Void, Item, Void>{
       List<Item> Itens;

       @Override
       protected void onPreExecute() {

        //your code

        list.setVisibility(View.GONE);
        adapterItem= new MyListAdapter(MyActivity.this, R.layout.item, new ArrayList<Item>());
        list.setAdapter(adapterItem);

        adapterItem.notifyDataSetChanged();
    }

    @Override
    protected Void doInBackground(Void... params) {

        Itens = getItens();
        for (Item item : Itens) {
            publishProgress(item );
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Item ... item ) {           
        adapterItem.add(item[0]);
    }

    @Override
    protected void onPostExecute(Void result) {
        //your code
        adapterItem.notifyDataSetChanged();     
        list.setVisibility(View.VISIBLE);
    }

}

}

1

私は同じ問題を抱えていて解決しました。私の問題はlistview、アレイアダプターとフィルターを備えたを使用していたことでした。このメソッドperformFilteringでは、データが含まれている配列をいじっていましたが、このメソッドはUIスレッドで実行されておらず、場合によってはいくつかの問題が発生するため問題でした。


1

このクラッシュの原因の1つは、ArrayListオブジェクトを完全に変更できないことです。したがって、アイテムを削除するときは、次のようにする必要があります。

mList.clear();
mList.addAll(newDataList);

これでクラッシュが修正されました。


1

私の場合、メインアクティビティGetFilter()TextWatcher()メソッドからアダプターのメソッドを呼び出し、Forループをオンにしてデータを追加しましたGetFilter()。解決策はAfterTextChanged()、メインアクティビティでForループをサブメソッドに変更し、GetFilter()


1
ソリューションを明確にしてください。私もu /のような状況になっています
nAkhmedov '27

1
adapter.notifyDataSetChanged()

2
Stack Overflowでは、ソリューションが機能する理由、または既存のソリューションよりも優れている理由に関する説明を追加することをお勧めします。詳細については、回答方法をご覧ください。
Samuel Liew

0

私もまったく同じエラーが発生し、AsyncTaskを使用していました:

`java.lang.IllegalStateException:` The content of the adapter has changed but ListView  did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131296334, class android.widget.ListView) with Adapter... etc

adapter.notifyDataSetChanged();UIスレッドの下部、つまりAsyncTask onPostExecuteメソッドに配置することで解決しました。このような :

 protected void onPostExecute(Void aVoid) {

 all my other stuff etc...
    all my other stuff etc...

           adapter.notifyDataSetChanged();

                }

            });
        }

これで私のアプリは動作します。

編集:実際、私のアプリはまだ10回に1回の割合でクラッシュし、同じエラーが発生しました。

結局、私はrunOnUiThread以前の投稿に出くわしました。したがって、次のようにdoInBackgroundメソッドに配置します。

@Override
protected Void doInBackground(Void... voids) {

    runOnUiThread(new Runnable() {
                      public void run() { etc... etc...

そして、adapter.notifyDataSetChanged();メソッドを削除しました 。今、私のアプリは決してクラッシュしません。


0

次の解決策のいずれかを試してください:

  1. スレッド(またはdoInBackgroundメソッド)のデータリストに新しいオブジェクトを追加すると、このエラーが発生することがあります。解決策は、一時リストを作成し、スレッド(またはdoInBackground)でこのリストにデータを追加してから、一時リストからUIスレッド(またはonPostExcute)でアダプターのリストにすべてのデータをコピーすることです。

  2. すべてのUI更新がUIスレッドで呼び出されることを確認します。


0

レイジーイメージローダーに新しいデータを追加すると、同じ問題が発生しました。

         adapter.notifyDataSetChanged();

       protected void onPostExecute(Void args) {
        adapter.notifyDataSetChanged();
        // Close the progressdialog
        mProgressDialog.dismiss();
         }

お役に立てば幸いです


0

@Mullinsが言ったように、「
アイテムを追加notifyDataSetChanged()し、UIスレッドで呼び出しました。これを解決しました。– Mullins」。

私の場合、私はメソッドasynctaskを呼び出しnotifyDataSetChanged()doInBackground()メソッドを呼び出しましたonPostExecute()が、例外を受け取ったときに問題が解決しました。


0

私はカスタムを持っていて、メソッドの終わりではなく最初ListAdapterに呼び出していsuper.notifyDataSetChanged()ました

@Override
public void notifyDataSetChanged() {
    recalculate();
    super.notifyDataSetChanged();
}

0

私は同じシチュエーションを持っていました、私は多くのボタングループをリストビューの私のアイテムのインサイトに持っていて、そして私はholder.rbVar.setOnclikのような私のアイテム内のいくつかのブール値を変更していました...

getView()内でメソッドを呼び出していたため、問題が発生しました。オブジェクトをsharepreference内に保存していたため、上記と同じエラーが発生しました

どのように解決したか。getView()内のメソッドを削除してnotifyDataSetInvalidated()にし、問題が解決しました

   @Override
    public void notifyDataSetChanged() {
        saveCurrentTalebeOnShare(currentTalebe);
        super.notifyDataSetChanged();
    }

0

同じ問題がありました。最後に私は解決策を得ました

リストビューを更新する前に、ソフトキーパッドが存在する場合は、最初に閉じてください。その後、データソースを設定し、notifydatasetchanged()を呼び出します。

キーパッドを内部で閉じると、リストビューはそのUIを更新します。キーパッドを閉じるまで呼び出しを続けます。そのときにデータソースが変更されると、この例外がスローされます。onActivityResultでデータが更新されている場合、同じエラーが発生する可能性があります。

 InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(v.getWindowToken(), 0);

        view.postDelayed(new Runnable() {
            @Override
            public void run() {
                refreshList();
            }
        },100L);

0

私の解決策:

1)を作成しtemp ArrayListます。

2)doInBackgroundメソッドで重い作業(sqlite行フェッチなど)を実行し、アイテムを一時配列リストに追加します。

3)temp araylistのすべての項目をonPostExecuteメソッドのリストビューのarraylistに追加します。

note:listviewからいくつかの項目を削除し、sqliteデータベースからも削除し、sdcardから項目に関連するいくつかのファイルを削除し、データベースから項目を削除して関連ファイルを削除し、それらをの一時配列リストに追加することができますbackground thread。次にUI thread、リストビューの配列リストから一時配列リストに存在する項目を削除します。

お役に立てれば。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.