Android, ListView IllegalStateException: “Bağdaştırıcının içeriği değişti, ancak ListView bir bildirim almadı”


189

Ne yapmak istiyorum : sonuçları hesaplanırken ListView içeriğini hesaplar ve ListView kısmen güncelleme bir arka plan iş parçacığı çalıştırın.

Ne kaçınmak zorunda biliyorum : Ben arka plan iş parçacığından ListAdapter içeriği ile karışıklık yapamam, bu yüzden AsyncTask devralındı ​​ve onProgressUpdate gelen sonuç (bağdaştırıcı girdileri ekleyin) yayımlayın. Bağdaştırıcım, sonuç nesnelerinin ArrayList'ini kullanır, bu dizicilerdeki tüm işlemler senkronize edilir.

Diğer insanların araştırması : Burada çok değerli veriler var . Ayrıca ~ 500 kullanıcı grubu için neredeyse günlük çökmelerden muzdarip oldum list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)ve onProgressUpdate'e blok eklediğimde, 10 kat azaltılmış ancak yok olmamış çökmeler. ( cevapta önerildi )

Bazen ne aldım : lütfen dikkat edin, nadiren gerçekleşir (3.5k kullanıcıdan biri için haftada bir kez). Ama bu hatadan tamamen kurtulmak istiyorum. İşte kısmi yığın izlemesi:

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

Yardım? Artık gerekmiyor, aşağıya bakın

SON CEVAP: Görünüşe göre , notifyDataSetChangedher 5 eklemeyi titremeyi ve ani liste değişikliklerini önlemek için çağırıyordum . Bu şekilde yapılamaz, temel liste değiştiğinde her zaman adaptöre haber verin. Bu böcek uzun zamandır benim için gitti.


3
NotifyDataSetChanged () öğesini çağırdınız mı?
Denis Palnitsky

1
Tabii ki, onProgressUpdate içinde diziyi gidiyor: (GİTTİ) list.setVisibility - Listede addObjectToList / senkronize operasyon / - notifyDataSetChanged () - list.setVisibility (GÖRÜNÜR) (ve görünürlük değişiklik yapılmadan istisna çok daha sık olur)
Tomash

1
Farklı bir iş parçacığında temel alınan ArrayList'i değiştiriyor musunuz? Bağdaştırıcı tarafından başvurulan ArrayList'te bile tüm değişikliklerin UI iş parçacığında gerçekleşmesi gerekir.
Zengin Schuler

2
@Qberticus - açıkça belirttiğim gibi, ArrayList farklı iş parçacığından değil, AcyncTask ProgressUpdate yönteminden - GUI iş parçacığında çalışır.
31'de tomash

1
@tomash i yenilemek için çekme kullanıyorum aynı istisna var ve sonrası yürütmek ben adaptör.notifyDataSetChanged () yazdım ; ) (lvAutherlist.completeRefreshing; ama bazen bu hatayı nasıl çözerim
Khan

Yanıtlar:


119

Aynı sorunu yaşadım.

ArrayListUI iş parçacığının dışına öğeler ekliyordum .

Çözüm: Her ikisini de yaptım adding the itemsve notifyDataSetChanged()UI iş parçacığında çağırdım .


4
Hem öğeleri ekledi ve UI iş parçacığında notifyDataSetChanged () çağırdı ve bu çözüldü.
Mullins

23
Genial yorum, gerçekten.
dentex

2
Bu bir Çok İş Parçacığı Sorunu ve Düzgün Eşitlenmiş Blokların Kullanılması Bu önlenebilir. UI İş Parçasına fazladan şeyler koymadan ve uygulamanın yanıt verme kaybına neden olmadan. Aşağıdaki cevabımı kontrol edin.
Javanator

2
Gelecekteki okuyucular için - arka plan iş parçacığında hesaplamalar yapmak iyi bir şeydir, ancak sonuçları UI iş parçacığına geçirmeli ve adaptör tabanı koleksiyonuna öğeler eklemeli ve aynı kod bloğunda adaptör bildirmelisiniz.
2014'te

4
@Mullins Lütfen çözümünüz için bir örnek gösterebilir misiniz? Aynı konuda takıldım
Jas

27

Aynı sorunu yaşadım, ancak yöntemi kullanarak düzelttim

requestLayout();

sınıftan ListView


35
bu yöntemi ne zaman çağırmalıyız?
JehandadK

3
ListDe öğeleri ekledikten veya bağdaştırıcısını değiştirdikten sonra @DeBuGGeR @.
Ahmet Noyan Kızıltan

ne zaman asynctask içinde eleman ekliyorum
Dr. aNdRO

2
Sadece kayıt için, @ Dr.aNdRO, sonuçlarınızı AsyncTask'ta topluyorsunuz. doInBackground () öğesine gidin ve sonuçları UI iş parçacığında çalışacak olan AsyncTask.onPostExecute () 'de güncellemek için kaydedin. Eğer varsa gerek gitmek, kullanım AsyncTask.publishProgress () ve aynı zamanda UI iş parçacığı çalışır AsyncTask.onProgressUpdate () olarak güncellemeye.
Nicole Borrelli

21

Bu bir Çoklu İş Parçacığı Sorunu ve Düzgün Eşitlenmiş Blokların Kullanılması Bu önlenebilir. UI İş Parçasına fazladan şeyler koymadan ve uygulamanın yanıt verme kaybına neden olmadan.

Ben de aynı şeyle karşılaştım. Ve en kabul edilen cevabın önerdiği gibi, UI Thread'den adaptör verilerinde değişiklik yapmak sorunu çözebilir. Bu işe yarayacak ama hızlı ve kolay bir çözüm ama en iyisi değil.

Gördüğünüz gibi normal bir durum. Veri bağdaştırıcısının arka plan iş parçacığından güncellenmesi ve UI iş parçacığında notifyDataSetChanged çağrılması çalışır.

Bu illegalStateException, bir ui iş parçacığı görünümü güncelleştirirken ve başka bir arka plan iş parçacığı verileri yeniden değiştirdiğinde ortaya çıkar. O an bu soruna neden oluyor.

Bu nedenle, adaptör verilerini değiştiren ve bildirimde bulunan veri değiştirme çağrısı yapan tüm kodu senkronize ederseniz. Bu konu gitmiş olmalı. Benim için gitti ve ben hala arka plan iş parçacığından veri güncelleme.

İşte başkalarının başvurması için benim durumuma özel kod.

Ana ekrandaki yükleyicim, telefon defteri kişilerini arka plandaki veri kaynaklarıma yüklüyor.

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

Bu PhoneBookManager.getPhoneBookContacts, telefon rehberinden kişiyi okur ve bunları hashmaps'ta doldurur. Liste Bağdaştırıcılarının liste çizmesi için doğrudan kullanılabilir.

Ekranımda bir düğme var. Bu, bu telefon numaralarının listelendiği bir etkinliği açar. Bir önceki iş parçacığı bitmeden önce listeden doğrudan setAdapter i yaparsanız hızlı naviagtion durumda olan çalışma daha az sıklıkta olur. Bu SO sorusunun başlığı olan istisna ortaya çıkar. Bu yüzden ikinci aktivitede böyle bir şey yapmak zorundayım.

İkinci aktivitedeki yükleyicim ilk iş parçacığının tamamlanmasını bekliyor. Bir ilerleme çubuğu gösterene kadar. Her iki yükleyicinin loadInBackground'sını kontrol edin.

Daha sonra bağdaştırıcıyı oluşturur ve ui iş parçacığında setAdapter çağırdığım etkinliğe teslim eder.

Bu benim sorunumu çözdü.

Bu kod yalnızca bir snippet'tir. Sizin için iyi derlemek için değiştirmeniz gerekir.

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

}

Bu yardımcı olur umarım


Pardon, bunu nasıl yaptın? Arka plan iş parçacığı kodunu ve notifydatasetchange çağrısını nasıl senkronize ettiniz? Bir AutocompleteTextView veri filtreleme yaparken bir doInBackground kullanıyorum ve aynı sorunla karşı karşıya ... Çözmek için bana bir şey tavsiye misiniz?
tonix

@ user3019105 - İki arka plan iş parçacığını senkronize etme. arka plandaki verileri güncelleyen ve diğeri ui iş parçacığını setAdadpter'e bildiren veya handler.post yöntemleri veya runOnUiThread yöntemleri kullanılarak değiştirilen verileri bildiren Özel durumum için iki yükleyiciyi doInBackground ile tek bir nesne üzerinde senkronize ettim. Bir başlangıç ​​ekranı üzerindeki verileri hızlı bir şekilde kullanıma hazır hale getirmek için açılış ekranındaki verileri hazırlamaya başlayın. Benim ihtiyacım buydu.
Javanator

Bu uygulamanın bir kod parçacığını gönderebilir misiniz? Benim durumumda, bağdaştırıcı üzerinde clear () çağrısını çözdüm, filtrelenmiş nesnelerin geçici ArrayList <T> oluşturma, sonra addAll (tmpArrayList) ve son olarak notifyDataSetChanged () çağrısı yaptım. Tüm bu çağrıları ana iş parçacığı üzerinde çalışan publishResult () filtre yöntemi içinde yapmak. Bunun güvenilir bir çözüm olduğunu düşünüyor musunuz?
tonix

@ user3019105 düzenlenen cevabı kontrol edin. Umarım şimdi size yardımcı olur
Javanator

bu cevap neden işe yaradığına dair çok fazla okuma gerektiriyor, tutorials.jenkov.com/java-concurrency/synchronized.html başlamak için iyi bir yer
abdu

15

Bunu 2 Liste ile çözdüm. Bir liste sadece adaptör için kullanıyorum ve diğer listedeki tüm veri değişikliklerini / güncellemelerini yapıyorum. Bu, bir arka plan iş parçacığında bir listede güncellemeler yapmamı ve ardından ana / UI iş parçacığında "bağdaştırıcı" listesini güncellememi sağlar:

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

Bu kodu yazdım ve ~ 12 saat boyunca 2.1 öykünücü görüntüsünde çalışmasını ve IllegalStateException alamadım. Android çerçevesine bu konuda şüphe yaratacak ve muhtemelen kodunuzda bir hata olduğunu söyleyeceğim. Umarım bu yardımcı olur. Belki de listenize ve verilerinize uyarlayabilirsiniz.

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

Çalışman için teşekkürler! ArrayAdapter yöntemi için hasStableIds () = false; benim uygulama satırları her notDataDataSetChanged () önce sıralandığı gibi, doğru değildi doğru döndü. Bir deneyeceğim - eğer çökmeler devam ederse araştırmaya devam edeceğim. Saygılarımla!
tomash

4

Birkaç gün önce aynı problemle karşılaştım ve günde binlerce binlerce çökmeye neden oldum, kullanıcıların yaklaşık% 0.1'i bu durumu karşılıyor. denedimsetVisibility(GONE/VISIBLE) ve requestLayout(), ancak çarpışma sayısı sadece biraz azalıyor.

Ve sonunda çözdüm. İle hiçbir şey setVisibility(GONE/VISIBLE). İle hiçbir şeyrequestLayout() .

Sonunda ben neden güncelleştirme verileri sonra Handleraramak notifyDataSetChanged()için kullanılan bir tür neden olabilir bulundu:

  1. Verileri bir model nesnesine günceller (buna DataSource diyorum)
  2. Kullanıcı dokunuşlar listview (çağırabilir hangi checkForTap()/ onTouchEvent()ve nihayet çağrıları layoutChildren())
  3. Bağdaştırıcı, model nesnesinden ve çağrı notifyDataSetChanged()ve güncelleme görünümlerinden veri alır

Ve bunu başka hata yaptı getCount(), getItem()vegetView() ben doğrudan ziyade adaptöre kopya onları daha DataSource alanları kullanabilirsiniz. Sonunda şu durumlarda çöküyor:

  1. Bağdaştırıcı, son yanıtın verdiği verileri günceller
  2. Bir sonraki yanıt geri döndüğünde, DataSource öğe sayısı değişikliğine neden olan verileri günceller
  3. Kullanıcı, hafifçe dokunma veya taşıma veya flip olabilen liste görünümüne dokunur
  4. getCount()ve getView()denir ve liste görünümü bulur verilerin tutarlı değildir ve benzeri istisnalar atar java.lang.IllegalStateException: The content of the adapter has changed but.... Başka bir yaygın istisna, IndexOutOfBoundExceptionüstbilgi / altbilgiyi kullanmanızdır ListView.

Bu yüzden çözüm kolaydır, işleyicim veri ve çağrı almak için adaptörü tetiklediğinde verileri DataSource'umdan adaptöre kopyalarım notifyDataSetChanged(). Kaza artık bir daha gerçekleşmiyor.


1
Bu benim sorunumdu. Benim durumumda, bildiriVeriSetChanged çağrılmadan önce getCount () kullanılabilir yeni sayıyı döndürüyordu. Listem 'sanal' olduğundan, artık notifyDataSetChanged yönteminde güncellenmiş sayıyı yakaladım ve sonra getCount () aracılığıyla sayım istendiğinde bu önbelleğe alınan sayıyı döndürdüm.
Glenn

3

Bu aralıklı olarak gerçekleşmiş olsaydı, bu sorunu yalnızca 'daha fazla yükle' son öğesi tıklandıktan sonra liste kaydırıldığında yaşadım. Liste kaydırılmamışsa, her şey yolunda gitti.

ÇOK hata ayıklamadan sonra, benim açımdan bir hata oldu, ancak Android kodunda da bir tutarsızlık oldu.

Doğrulama gerçekleştiğinde, bu kod ListView'da yürütülür

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

Ancak onChange gerçekleştiğinde, bu kodu AdapterView'da (ListView'in üst öğesi) tetikler

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

Adaptörün Aynı olduğu garanti edilemediğine dikkat edin!

Benim durumumda, bir 'LoadMoreAdapter' olduğundan getAdapter çağrısında WrappedAdapter döndürüyordu (temel nesnelere erişim için). Bu, fazladan 'Daha Fazla Yükle' öğesi ve İstisna atılmasından dolayı sayımların farklı olmasına neden oldu.

Bunu sadece yaptım çünkü dokümanlar bunu yapmanın uygun olduğunu gösteriyor

ListView.getAdapter javadoc

Bu ListView'da kullanılmakta olan bağdaştırıcıyı döndürür. Döndürülen bağdaştırıcı, setAdapter'a (ListAdapter) iletilen bağdaştırıcıyla aynı olmayabilir, ancak bir WrapperListAdapter olabilir.


Ben benzer bir sorun yaşıyorum . Lütfen nasıl düzelteceğinizi önerebilir misiniz?
Mayıs13ank

Yukarıdaki 2 kod örneğine bakarsanız, ListView'da mAdapter ve ListView (AdapterView) öğesinin üst öğesinde getAdapter () öğesini görürsünüz. GetAdapter () yöntemini geçersiz kılarsanız, getAdapter () öğesinin sonucu mAdapter olmayabilir. 2 Adaptörün sayısı farklıysa, hatayı alırsınız.
aaronvargas

3

Benim sorunum ListView ile birlikte bir Filtre kullanımı ile ilgili.

Ayar veya ListView temel veri modeli güncellerken, böyle bir şey yapıyordum:

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

Arayan filter()son satırında olacak (ve zorunluluk) neden notifyDataSetChanged()Filtrenin içinde çağrılacak publishResults()yöntemle. Bu bazen hızlı Nexus 5 cihazımda bazen işe yarayabilir. Ancak gerçekte, daha yavaş cihazlarla veya kaynak yoğun koşullarda fark edeceğiniz bir hatayı gizliyor.

Sorun, filtrelemenin eşzamansız olarak yapılması ve böylece filter()ifadenin sonu ile publishResults()hem UI iş parçacığında yapılan çağrı arasında, diğer bazı UI iş parçacığı kodunun çalıştırılabilir ve bağdaştırıcının içeriğini değiştirebilmesidir.

Gerçek düzeltme kolaydır, notifyDataSetChanged()filtrelemenin yapılmasını istemeden önce de arayın :

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

3

Feed nesneleri varsa bir List var. Yok-UI iş parçacığından eklenir ve kesilir. Aşağıdaki adaptör ile iyi çalışır. FeedAdapter.notifyDataSetChangedYine de UI iş parçacığında çağırıyorum ama biraz sonra. Bunu yapıyorum çünkü Feed nesnelerim UI öldüğünde bile Yerel Hizmet'te bellekte kalıyor.

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

Tam olarak aynı hata günlüğü ile aynı sorunla karşı karşıyaydım. Benim durumumda onProgress()AsyncTask kullanarak değerleri bağdaştırıcıya ekler mAdapter.add(newEntry). Daha az duyarlı Ben seti haline UI önlemek için mAdapter.setNotifyOnChange(false)ve çağrı mAdapter.notifyDataSetChanged()4 kez ikinci. Saniyede bir dizi dizilir.

Bu iyi çalışır ve çok bağımlılık yapar, ancak maalesef gösterilen liste öğelerine yeterince sık dokunarak çökmesi mümkündür.

Ama öyle görünüyor ki kabul edilebilir bir çözüm buldum. Sadece ui iş parçacığı üzerinde çalışsanız bile, bağdaştırıcı çağrılmadan verilerinde birçok değişiklik kabul etmiyor notifyDataSetChanged()olsa da, bu nedenle belirtilen 300ms bitene kadar tüm yeni öğeleri depolayan bir kuyruk oluşturdum. Bu an ulaşılırsa, depolanan tüm öğeleri tek bir çekimde ekler ve ararım notifyDataSetChanged(). Şimdiye kadar listeyi çökertemedim .



2

XMPP bildirim uygulamamda da aynı sorunla karşılaşmış olsam bile, alıcıların mesajının liste görünümüne (ile birlikte ArrayList) geri eklenmesi gerekiyor . Alıcı içeriğini MessageListener(ayrı iş parçacığı) aracılığıyla eklemeye çalıştığımda , uygulama yukarıdaki hatayla sonlandırılıyor. Bunu , Activity sınıfının bir parçası olan my arraylist& setListviewadapaterthrough runOnUiThreadyöntemine içerik ekleyerek çözdüm. Bu benim sorunumu çözdü.


1

Benzer bir sorunla karşılaştım, işte benim durumumda bu şekilde çözdüm. taskZaten olup olmadığını RUNNINGveya FINISHEDbir görevin yalnızca bir kez çalışabildiğini doğrularım . Aşağıda çözümümden kısmi ve uyarlanmış bir kod göreceksiniz.

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

Aynı sorunu yaşadım ve çözdüm. Benim sorunum listviewbir dizi adaptörü ve filtre ile bir , kullanıyordu oldu . Yöntemin günü performFilteringben veri var dizisi ile karıştırmasını ve yöntem UI iş parçacığı üzerinde çalışmadığı ve SONUNDA bazı sorunlar ortaya çıkarmaktadır beri sorun oldu.


1

Bu çökmenin bir nedeni, ArrayListnesnenin tamamen değiştirilememesidir. Yani, bir öğeyi kaldırdığımda, bunu yapmak zorundayım:

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

Bu benim için çökmeyi düzeltti.


1

Benim durumumda yöntemi ana faaliyet yönteminden GetFilter()bir bağdaştırıcı üzerinde çağırdı ve TextWatcher()bir For döngüsü açık olan verileri ekledim GetFilter(). Çözüm, AfterTextChanged()ana Etkinlikte For döngüsünü alt yönteme değiştirmek ve çağrıyı silmekGetFilter()


1
Lütfen çözümünüzü netleştirin. Ben de durumdayım u /
nAkhmedov


0

Ayrıca tamamen aynı hatayı alıyordu ve AsyncTask kullanarak:

`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

Ben adapter.notifyDataSetChanged();benim AsyncTask onPostExecute yöntemi benim UI iş parçacığının altına koyarak çözüldü . Bunun gibi :

 protected void onPostExecute(Void aVoid) {

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

           adapter.notifyDataSetChanged();

                }

            });
        }

Şimdi uygulamam çalışıyor.

EDIT: Aslında, benim app hala her 10 yaklaşık 1 kez aynı hatayı vererek çöktü.

Sonunda runOnUiThread, yararlı olabileceğini düşündüğüm bir önceki yazıya rastladım . Bu yüzden doInBackground yöntemime koydum, şöyle:

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

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

Ve adapter.notifyDataSetChanged();yöntemi kaldırdım . Şimdi, uygulamam asla kilitlenmiyor.


0

Lütfen şu çözümlerden birini deneyin:

  1. Bazen, bir iş parçacığındaki (veya doInBackgroundyöntemdeki) veri listesine yeni nesne eklerseniz , bu hata oluşur. Çözüm şudur: geçici bir liste oluşturmak ve bu listeye iş parçacığı (veya doInBackground) içinde veri eklemek , ardından geçici listeden tüm verileri UI iş parçacığındaki (veya onPostExcute) bağdaştırıcı listesine kopyalamak

  2. UI iş parçacığında tüm UI güncellemelerinin çağrıldığından emin olun.


0

ben sadece koymak tembel görüntü yükleyici yeni veri eklemek aynı sorunu çözdü

         adapter.notifyDataSetChanged();

içinde

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

umarım sana yardımcı olur


0

@Mullins'in dediği gibi "İkisini
de ekledim ve notifyDataSetChanged()kullanıcı arayüzünde aradım ve bunu çözdüm . - Mullins".

Benim durumumda ben var asynctaskve ben denilen notifyDataSetChanged()içinde doInBackground()yöntem ve I olarak adlandırılan sorun, çözüldü onPostExecute()Ben özel durum aldı.


0

Bir gelenek vardı ve yöntemin sonunda değil, başında ListAdapterçağırıyordumsuper.notifyDataSetChanged()

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

0

Ben aynı durum vardı, benim liste görünümünde öğemi insite birçok düğme grubu vardı ve ben item.rbVar.setOnclik gibi benim öğe içinde bazı boole değerleri değişiyordu ...

getView () içinde bir yöntem çağırıyordu çünkü benim sorunum oluştu; ve sharepreference içindeki bir nesneyi kaydediyordu, bu yüzden yukarıda aynı hatayla karşılaştım

Nasıl çözdüm; VeriSetInvalidated () bildirmek için getView () içinde benim yöntem kaldırıldı ve sorun gitti

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

0

aynı problemi yaşadım. sonunda çözümü buldum

Liste görünümünü güncellemeden önce, yazılım tuş takımı varsa önce kapatın. Bundan sonra veri kaynağını ayarlayın ve notifydatasetchanged () öğesini çağırın.

tuş takımı dahili olarak listview kapatılırken kullanıcı arayüzü güncellenecektir. tuş takımını kapatana kadar aramaya devam eder. veri kaynağı değiştiğinde bu istisnayı atar. onActivityResult içinde veri güncelleniyorsa, aynı hata olasılığı vardır.

 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

Çözümüm:

1) oluşturun temp ArrayList.

2) ağır işlerinizi (sqlite satır getirme, ...) doInBackground yöntemle yapın ve temp arraylist'e öğeler ekleyin.

3) geçici araylistten tüm öğeleri listenizdeki arraylist'e onPostExecuteyöntemle ekleyin .

note:bazı öğeleri liste görünümünden silmek ve ayrıca sqlite veritabanından silmek ve belki de sdcard'dan öğelerle ilgili bazı dosyaları silmek, öğeleri veritabanından kaldırmak ve ilgili dosyaları kaldırmak ve geçici arraylist'e eklemek isteyebilirsiniz background thread. daha sonra UI threadtemp arraylist öğesinde mevcut olan öğeleri listview arraylist öğesinden silin.

Bu yardımcı olur umarım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.