Android, ListView IllegalStateException: "Konten adaptor telah berubah tetapi ListView tidak menerima pemberitahuan"


189

Yang ingin saya lakukan : menjalankan utas latar belakang yang menghitung konten ListView dan memperbarui ListView sebagian, sementara hasilnya dihitung.

Yang saya tahu harus saya hindari : Saya tidak bisa main-main dengan konten ListAdapter dari utas latar belakang, jadi saya mewarisi AsyncTask dan menerbitkan hasil (menambahkan entri ke adaptor) dari onProgressUpdate. Adaptor saya menggunakan ArrayList objek hasil, semua operasi pada daftar array disinkronkan.

Penelitian orang lain : ada data yang sangat berharga di sini . Saya juga menderita crash hampir setiap hari untuk sekelompok ~ 500 pengguna, dan ketika saya menambahkan list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)blok di onProgressUpdate, crash diturunkan dengan faktor 10 tetapi tidak hilang. (disarankan dalam jawaban )

Apa yang saya dapatkan kadang-kadang : tolong perhatikan, itu terjadi sangat jarang (seminggu sekali untuk salah satu dari pengguna 3.5k). Tapi saya ingin menyingkirkan bug ini sepenuhnya. Ini adalah stacktrace parsial:

`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)
[...]

Tolong? Tidak diperlukan lagi, lihat di bawah

JAWABAN FINAL: Ternyata, saya menelepon notifyDataSetChangedsetiap 5 insersi untuk menghindari perubahan daftar yang tiba-tiba berkedip. Itu tidak bisa dilakukan dengan cara seperti itu, selalu beri tahu adaptor ketika daftar dasar berubah. Bug ini sudah lama hilang untuk saya sekarang.


3
Apakah Anda menelepon notifyDataSetChanged ()?
Denis Palnitsky

1
Tentu saja, di onProgressUpdate ada urutan: list.setVisibility (GONE) - addObjectToList / operasi yang disinkronkan pada daftar / - notifyDataSetChanged () - list.setVisibility (VISIBLE) (dan tanpa visibilitas modifikasi pengecualian terjadi jauh lebih sering)
tomash

1
Apakah Anda memodifikasi ArrayList yang mendasarinya pada utas yang berbeda? Semua perubahan, bahkan ArrayList yang dirujuk oleh Adaptor, harus terjadi pada utas UI.
Rich Schuler

2
@Qberticus - seperti yang saya jelaskan, saya tidak memodifikasi ArrayList dari utas yang berbeda, tetapi dari metode onProgressUpdate dari AcyncTask - ia bekerja pada utas GUI.
tomash

1
@tomash saya mendapat pengecualian yang sama saya menggunakan tarikan untuk menyegarkan dan pada posting jalankan saya telah menulis adapter.notifyDataSetChanged (); lvAutherlist.completeRefreshing (); tapi terkadang ada kesalahan ini bagaimana cara mengatasinya
Khan

Jawaban:


119

Saya memiliki masalah yang sama.

Saya menambahkan item ke ArrayListluar saya di utas UI.

Solusi: Saya telah melakukan keduanya, adding the itemsdan memanggil notifyDataSetChanged()thread UI.


4
Saya menambahkan item dan memanggil notifyDataSetChanged () di utas UI dan saya menyelesaikannya.
Mullins

23
Komentar ramah, sungguh.
dentex

2
Ini adalah Masalah Multithreading dan Menggunakan Blok yang Disinkronkan dengan Benar Ini dapat dicegah. Tanpa meletakkan hal-hal tambahan di UI Thread dan menyebabkan hilangnya respons aplikasi. Periksa jawaban saya di bawah ini.
Javanator

2
Untuk pembaca di masa mendatang - melakukan perhitungan di utas latar adalah hal yang baik, tetapi Anda harus meneruskan hasilnya ke utas UI dan melakukan penambahan item ke koleksi basis adaptor dan memberi tahu adaptor dalam blok kode yang sama.
tomash

4
@ Mullins Bisakah Anda tolong tunjukkan sampel untuk solusi Anda? Saya terjebak pada masalah yang sama
Jas

27

Saya memiliki masalah yang sama, tetapi saya memperbaikinya menggunakan metode ini

requestLayout();

dari kelas ListView


35
kapan kita harus memanggil metode ini?
JehandadK

3
@DeBuGGeR setelah Anda menambahkan item ke ListView atau mengubah adaptornya.
Ahmet Noyan Kızıltan

bagaimana jika saya menambahkan elemen di asynctask
Dr. aNdRO

2
Sebagai catatan, @ Dr.aNdRO, Anda mengumpulkan hasil Anda di AsyncTask. doInBackground () dan simpan hasil Anda untuk memperbarui daftar di AsyncTask.onPostExecute (), yang akan berjalan di utas UI. Jika Anda perlu memperbarui saat menggunakan, gunakan AsyncTask.publishProgress () dan AsyncTask.onProgressUpdate () yang juga berjalan di utas UI.
Nicole Borrelli

21

Ini adalah Masalah MultiThreading dan Menggunakan Blok yang Disinkronkan Dengan Benar Ini dapat dicegah. Tanpa meletakkan hal-hal tambahan di UI Thread dan menyebabkan hilangnya respons aplikasi.

Saya juga menghadapi hal yang sama. Dan sebagai jawaban yang paling diterima menyarankan membuat perubahan ke data adaptor dari UI Thread dapat menyelesaikan masalah. Itu akan berhasil tetapi merupakan solusi yang cepat dan mudah tetapi bukan yang terbaik.

Seperti yang Anda lihat untuk kasus normal. Memperbarui adaptor data dari utas latar belakang dan memanggil notifyDataSetChanged di utas UI berfungsi.

IllegalStateException ini muncul ketika utas ui memperbarui tampilan dan utas latar belakang lainnya mengubah data lagi. Momen itu yang menyebabkan masalah ini.

Jadi, jika Anda akan menyinkronkan semua kode yang mengubah data adaptor dan membuat panggilan notifydatasetchange. Masalah ini harus hilang. Seperti yang terjadi pada saya dan saya masih memperbarui data dari utas latar belakang.

Ini adalah kode khusus kasus saya untuk dirujuk oleh orang lain.

Pemuat saya di layar utama memuat kontak buku telepon ke sumber data saya di latar belakang.

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

PhoneBookManager ini. GetPhoneBookContacts membaca kontak dari buku telepon dan mengisinya dalam hashmaps. Yang secara langsung dapat digunakan untuk Daftar Adaptor untuk menggambar daftar.

Ada tombol di layar saya. Itu membuka aktivitas di mana nomor telepon ini terdaftar. Jika saya langsung mengaturAdapter di atas daftar sebelum utas sebelumnya menyelesaikan pekerjaannya yang merupakan kasus naviagtion cepat lebih jarang terjadi. Itu muncul pengecualian. Yang merupakan judul dari pertanyaan SO ini. Jadi saya harus melakukan sesuatu seperti ini di aktivitas kedua.

Loader saya di aktivitas kedua menunggu untuk menyelesaikan utas pertama. Sampai ini menunjukkan bilah kemajuan. Periksa loadInBackground dari kedua loader.

Kemudian ia menciptakan adaptor dan mengirimkannya ke aktivitas di mana pada thread ui saya sebut setAdapter.

Itu memecahkan masalah saya.

Kode ini hanya potongan. Anda perlu mengubahnya untuk dikompilasi dengan baik untuk Anda.

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

}

Semoga ini membantu


Maaf, bagaimana Anda melakukannya? Bagaimana Anda menyinkronkan kode utas latar belakang dan panggilan notifydatasetchange? Saya menggunakan doInBackground saat memfilter data pada AutocompleteTextView dan saya menghadapi masalah yang sama ... Bisakah Anda memberi tahu saya sesuatu untuk diselesaikan?
tonix

@ user3019105 - Menyinkronkan dua utas latar belakang. salah satu yang memperbarui data di latar belakang dan lainnya yang memberi tahu utas ui untuk mengaturAdadpter atau notifydatadisusun dengan menggunakan metode handler.post atau menjalankan metode OnUiThread tersedia. Untuk kasus khusus saya, saya menyinkronkan dua loader dengan doInBackground mereka disinkronkan pada objek tunggal. Satu mulai mempersiapkan data pada layar splash itu sendiri agar tersedia dengan cepat untuk layar dasbor. Itulah kebutuhan saya.
Javanator

Bisakah Anda memposting potongan kode implementasi ini? Dalam kasus saya, saya telah memutuskan memanggil clear () pada adaptor, membuat ArrayList <T> sementara dari objek yang difilter, kemudian melakukan panggilan ke addAll (tmpArrayList) dan akhirnya notifyDataSetChanged (). Saya membuat semua panggilan ini di dalam metode Filter publishResult (), berjalan di utas utama. Apakah Anda pikir ini solusi yang dapat diandalkan?
tonix

@ user3019105 periksa jawaban yang diedit. Semoga bermanfaat bagi Anda sekarang
Javanator

jawaban ini membutuhkan banyak bacaan tentang mengapa ia bekerja, tutorials.jenkov.com/java-concurrency/synchronized.html adalah tempat yang baik untuk memulai
abdu

15

Saya memecahkan ini dengan memiliki 2 Daftar. Satu daftar yang saya gunakan hanya untuk adaptor, dan saya melakukan semua perubahan / pembaruan data pada daftar lainnya. Ini memungkinkan saya untuk melakukan pembaruan pada satu daftar di utas latar belakang, dan kemudian memperbarui daftar "adaptor" di utas utama / 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

Saya menulis kode ini dan menjalankannya dalam gambar 2,1 emulator selama ~ 12 jam dan tidak mendapatkan IllegalStateException. Saya akan memberikan kerangka kerja android manfaat dari keraguan yang satu ini dan mengatakan bahwa kemungkinan besar kesalahan dalam kode Anda. Saya harap ini membantu. Mungkin Anda bisa menyesuaikannya dengan daftar dan data Anda.

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

Terima kasih atas kerjamu! Saya perhatikan bahwa untuk metode ArrayAdapter hasStableIds () = false; implementasi saya kembali benar yang tidak tepat, karena baris diurutkan sebelum setiap notifyDataSetChanged (). Saya akan mencobanya - jika crash tetap ada, saya akan melanjutkan penyelidikan. Salam Hormat!
tomash

4

Beberapa hari yang lalu saya menemukan masalah yang sama dan menyebabkan beberapa ribu crash per hari, sekitar 0,1% pengguna memenuhi situasi ini. Saya mencoba setVisibility(GONE/VISIBLE)dan requestLayout(), tetapi jumlah macet hanya berkurang sedikit.

Dan saya akhirnya menyelesaikannya. Tidak ada apa-apa dengan setVisibility(GONE/VISIBLE). Tidak ada apa-apa dengan requestLayout().

Akhirnya saya menemukan alasannya adalah saya menggunakan Handlerpanggilan notifyDataSetChanged()setelah pembaruan data, yang dapat menyebabkan semacam:

  1. Memperbarui data ke objek model (Saya menyebutnya DataSource)
  2. Pengguna menyentuh tampilan daftar (yang dapat memanggil checkForTap()/ onTouchEvent()dan akhirnya memanggil layoutChildren())
  3. Adaptor mendapat data dari objek model dan memanggil notifyDataSetChanged()dan memperbarui tampilan

Dan saya membuat kesalahan lain dalam getCount(), getItem()dangetView() , saya langsung menggunakan bidang di DataSource, daripada menyalinnya ke adaptor. Jadi akhirnya crash ketika:

  1. Adaptor memperbarui data yang diberikan respons terakhir
  2. Ketika respons berikutnya kembali, DataSource memperbarui data, yang menyebabkan perubahan jumlah item
  3. Pengguna menyentuh tampilan daftar, yang mungkin berupa ketukan atau gerakan atau flip
  4. getCount()dan getView()dipanggil, dan listview menemukan data tidak konsisten, dan melempar pengecualian seperti java.lang.IllegalStateException: The content of the adapter has changed but.... Pengecualian umum lainnya adalah IndexOutOfBoundExceptionjika Anda menggunakan header / footer di ListView.

Jadi solusinya mudah, saya hanya menyalin data ke adaptor dari DataSource saya ketika Handler saya memicu adaptor untuk mendapatkan data dan panggilan notifyDataSetChanged(). Kecelakaan sekarang tidak pernah terjadi lagi.


1
Ini masalah saya. Dalam kasus saya getCount () mengembalikan jumlah baru yang tersedia sebelum notifyDataSetChanged dipanggil. Karena daftar saya adalah 'virtual', saya sekarang menangkap jumlah yang diperbarui dalam metode notifyDataSetChanged saya dan kemudian mengembalikan jumlah yang di-cache ini ketika diminta menghitung melalui getCount ().
Glenn

3

Seandainya ini terjadi sesekali, ternyata saya hanya memiliki masalah ini ketika daftar telah digulir setelah item terakhir 'memuat lebih banyak' diklik. Jika daftar tidak digulir, semuanya bekerja dengan baik.

Setelah BANYAK debugging, itu adalah bug di pihak saya, tetapi juga ada ketidakkonsistenan dalam kode Android.

Ketika validasi terjadi, kode ini dieksekusi di 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 "

Tetapi ketika onChange terjadi, kode ini diaktifkan di AdapterView (induk dari ListView)

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

Perhatikan cara Adaptor TIDAK dijamin Sama!

Dalam kasus saya, karena itu adalah 'LoadMoreAdapter' saya mengembalikan WrappedAdapter dalam panggilan getAdapter (untuk akses ke objek yang mendasarinya). Ini menghasilkan jumlah yang berbeda karena item 'Muat Lebih Banyak' tambahan dan Pengecualian dilemparkan.

Saya hanya melakukan ini karena dokumen membuatnya sepertinya tidak apa-apa untuk dilakukan

ListView.getAdapter javadoc

Mengembalikan adaptor yang saat ini digunakan dalam ListView ini. Adaptor yang dikembalikan mungkin bukan adaptor yang sama yang dilewatkan ke setAdapter (ListAdapter) tetapi mungkin WrapperListAdapter.


Saya mengalami masalah serupa. Bisakah Anda menyarankan cara memperbaikinya?
13

Jika Anda melihat 2 contoh kode di atas, Anda akan melihat mAdapter di ListView dan getAdapter () di induk dari ListView (AdapterView). Jika Anda mengganti getAdapter (), hasil getAdapter () mungkin bukan mAdapter. Jika jumlah dari 2 Adaptor berbeda, Anda mendapatkan kesalahan.
aaronvargas

3

Masalah saya terkait dengan penggunaan Filter bersama dengan ListView.

Saat mengatur atau memperbarui model data yang mendasari ListView, saya melakukan sesuatu seperti ini:

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

Memanggil filter()di baris terakhir akan (dan harus) menyebabkan notifyDataSetChanged()dipanggil dalam FilterpublishResults() . Terkadang ini bekerja dengan baik, khususnya di Nexus 5. saya yang cepat. Namun pada kenyataannya, ini menyembunyikan bug yang akan Anda perhatikan dengan perangkat yang lebih lambat atau dalam kondisi intensif sumber daya.

Masalahnya adalah bahwa penyaringan dilakukan secara tidak sinkron, dan dengan demikian antara akhir filter()pernyataan dan panggilan untuk publishResults(), keduanya di utas UI, beberapa kode utas UI lainnya dapat menjalankan dan mengubah konten adaptor.

Cara memperbaiki yang sebenarnya mudah, cukup panggil notifyDataSetChanged()juga sebelum meminta penyaringan dilakukan:

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

3

Saya memiliki Daftar jika objek Feed. Itu ditambahkan dan terpotong dari utas UI. Ini berfungsi dengan baik dengan adaptor di bawah ini. Saya tetap memanggil FeedAdapter.notifyDataSetChangedutas UI tetapi sedikit kemudian. Saya memang suka ini karena objek Feed saya tetap tersimpan dalam memori di Layanan Lokal bahkan ketika UI mati.

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

Saya menghadapi masalah yang sama dengan log kesalahan yang persis sama. Dalam kasus saya onProgress()tentang AsyncTask menambahkan nilai ke adaptor menggunakan mAdapter.add(newEntry). Untuk menghindari UI menjadi kurang responsif, saya mengatur mAdapter.setNotifyOnChange(false)dan memanggil mAdapter.notifyDataSetChanged()4 kali kedua. Sekali per detik array diurutkan.

Ini berfungsi dengan baik dan terlihat sangat membuat ketagihan, tetapi sayangnya sangat mungkin untuk menghentikannya dengan menyentuh item daftar yang ditampilkan cukup sering.

Tapi sepertinya saya telah menemukan solusi yang dapat diterima. Dugaan saya bahwa bahkan jika Anda hanya bekerja pada utas ui adaptor tidak menerima banyak perubahan pada data itu tanpa menelepon notifyDataSetChanged(), karena ini saya membuat antrian yang menyimpan semua item baru sampai 300ms yang disebutkan selesai. Jika saat ini tercapai, saya menambahkan semua item yang disimpan dalam satu kesempatan dan panggilannotifyDataSetChanged() . Sampai sekarang saya tidak dapat lagi crash daftar .



2

Bahkan saya menghadapi masalah yang sama dalam aplikasi notifikasi XMPP saya, pesan penerima perlu ditambahkan kembali ke tampilan daftar (diimplementasikan dengan ArrayList). Ketika saya mencoba menambahkan konten penerima melalui MessageListener(utas terpisah), aplikasi berhenti dengan kesalahan di atas. Saya memecahkan ini dengan menambahkan konten ke metode saya arraylist& setListviewadapatermelalui runOnUiThreadyang merupakan bagian dari kelas Activity. Ini menyelesaikan masalah saya.


1

Saya menghadapi masalah yang sama, inilah cara saya memecahkan dalam kasus saya. Saya memverifikasi apakah tasksudah RUNNINGatau FINISHEDkarena tugas hanya dapat dijalankan sekali. Di bawah ini Anda akan melihat kode parsial dan diadaptasi dari solusi saya.

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

Saya memiliki masalah yang sama dan saya menyelesaikannya. Masalah saya adalah bahwa saya menggunakan listview, dengan adaptor array dan dengan filter. Pada metode ini performFilteringsaya mengacaukan array yang memiliki data dan itu adalah masalah karena metode ini tidak berjalan pada utas UI dan AKHIRNYA memunculkan beberapa masalah.


1

Salah satu penyebab kecelakaan ini adalah bahwa ArrayListobjek tidak dapat berubah sepenuhnya. Jadi, ketika saya menghapus item, saya harus melakukan ini:

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

Ini memperbaiki crash untuk saya.


1

Dalam kasus saya, saya memanggil metode GetFilter()pada adaptor dari TextWatcher()metode pada Kegiatan utama, dan saya menambahkan data dengan For loop on GetFilter(). Solusinya adalah mengubah loop Untuk AfterTextChanged()sub metode pada Kegiatan utama dan menghapus panggilan keGetFilter()


1
Harap jelaskan solusi Anda. Saya juga dalam situasi seperti u /
nAkhmedov

1
adapter.notifyDataSetChanged()

2
Ini praktik yang baik di Stack Overflow untuk menambahkan penjelasan mengapa solusi Anda harus bekerja atau lebih baik daripada solusi yang ada. Untuk informasi lebih lanjut baca Cara Menjawab .
Samuel Liew

0

Saya juga mendapatkan kesalahan yang sama persis dan menggunakan 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

Saya menyelesaikannya dengan meletakkan adapter.notifyDataSetChanged();di bagian bawah utas UI saya, yaitu metode onyncTask AsyncTask saya. Seperti ini :

 protected void onPostExecute(Void aVoid) {

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

           adapter.notifyDataSetChanged();

                }

            });
        }

Sekarang aplikasi saya berfungsi.

EDIT: Faktanya, aplikasi saya masih crash setiap 1 dari 10 kali, memberikan kesalahan yang sama.

Akhirnya saya menemukan runOnUiThreadposting sebelumnya, yang saya pikir bisa bermanfaat. Jadi saya memasukkannya ke dalam metode doInBackground saya, seperti ini:

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

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

Dan saya menghapus adapter.notifyDataSetChanged();metodenya. Sekarang, aplikasi saya tidak pernah macet.


0

Silakan coba salah satu solusi ini:

  1. Terkadang, jika Anda menambahkan objek baru ke daftar data di utas (atau doInBackgroundmetode), kesalahan ini akan terjadi. Solusinya adalah: buat daftar sementara dan lakukan menambahkan data ke daftar ini di utas (atau doInBackground), kemudian lakukan menyalin semua data dari daftar sementara ke daftar adaptor di utas UI (atau onPostExcute)

  2. Pastikan semua pembaruan UI disebut di utas UI.


0

Saya memiliki masalah yang sama ketika menambahkan data baru di lazy image loader yang baru saja saya taruh

         adapter.notifyDataSetChanged();

di

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

semoga membantu anda


0

Seperti @Mullins berkata, "
Saya menambahkan item dan memanggil notifyDataSetChanged()thread UI dan saya menyelesaikan ini. - Mullins".

Dalam kasus saya saya punya asynctaskdan saya disebut notifyDataSetChanged()dalam doInBackground()metode dan masalah diselesaikan, ketika saya menelepon dari onPostExecute()saya menerima pengecualian.


0

Saya punya kebiasaan ListAdapterdan menelepon super.notifyDataSetChanged()di awal dan bukan di akhir metode

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

0

Saya memiliki sittuation yang sama, saya memiliki banyak buttongroup menyisipkan item saya di listview dan saya mengubah beberapa nilai boolean di dalam item saya seperti holder.rbVar.setOnclik ...

masalah saya terjadi karena saya memanggil metode di dalam getView (); dan sedang menyimpan objek di dalam Sharepreference, jadi saya memiliki kesalahan yang sama di atas

Bagaimana saya memecahkannya; Saya menghapus metode saya di dalam getView () untuk notifyDataSetInvalidated () dan masalah hilang

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

0

Saya memiliki masalah yang sama. akhirnya saya mendapat solusinya

sebelum memperbarui tampilan daftar, jika tombol lunak hadir, tutup terlebih dahulu. setelah itu atur sumber data dan panggil notifydatasetchanged ().

sambil menutup keypad, listview internal akan memperbarui ui-nya. itu terus memanggil sampai menutup tombol. waktu itu jika sumber data berubah maka saya akan melemparkan pengecualian ini. jika data diperbarui di onActivityResult, ada kemungkinan kesalahan yang sama.

 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

Solusi saya:

1) buat a temp ArrayList.

2) lakukan pekerjaan berat Anda (sqlite row fetch, ...) dalam doInBackgroundmetode dan tambahkan item ke temp arraylist.

3) tambahkan semua item dari temp araylist ke daftar array listview Anda dalam onPostExecutemetode.

note:Anda mungkin ingin menghapus beberapa item dari listview dan juga menghapus dari database sqlite dan mungkin menghapus beberapa file yang terkait dengan item dari sdcard, cukup hapus item dari database dan hapus file terkait mereka dan tambahkan mereka ke temp arraylist di background thread. lalu masukUI thread hapus item yang ada di temp arraylist dari daftar listview itu.

Semoga ini membantu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.