Android, ListView IllegalStateException: "एडेप्टर की सामग्री बदल गई है, लेकिन सूची दृश्य को सूचना नहीं मिली"


188

मैं क्या करना चाहता हूं : एक पृष्ठभूमि थ्रेड चलाएं जो सूची दृश्य सामग्री की गणना करता है और सूची दृश्य को आंशिक रूप से अपडेट करता है, जबकि परिणाम की गणना की जाती है।

मुझे पता है कि मुझे बचना है : मैं पृष्ठभूमि थ्रेड से ListAdapter सामग्री के साथ गड़बड़ नहीं कर सकता, इसलिए मुझे AsProTask विरासत में मिला और onProgressUpdate से परिणाम (एडॉप्टर में प्रविष्टियां जोड़ें) प्रकाशित किया। मेरे एडॉप्टर परिणाम वस्तुओं के ArrayList का उपयोग करता है, उन सरणीविस्ट पर सभी ऑपरेशन सिंक्रनाइज़ हैं।

अन्य लोगों का शोध : यहां बहुत मूल्यवान डेटा है । मुझे ~ 500 उपयोगकर्ताओं के समूह के लिए लगभग दैनिक दुर्घटनाओं का सामना करना पड़ा, और जब मैंने list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)onProgressUpdate में ब्लॉक जोड़ा , तो क्रैश 10 के एक कारक से कम हो गया लेकिन गायब नहीं हुआ। (यह जवाब में सुझाव दिया गया था )

मुझे कभी-कभी मिला : कृपया ध्यान दें, यह वास्तव में शायद ही कभी होता है (3.5k उपयोगकर्ताओं में से एक के लिए सप्ताह में एक बार)। लेकिन मैं इस बग से पूरी तरह छुटकारा पाना चाहूंगा। यहाँ आंशिक स्टैकट्रेस है:

`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
क्या आपने InformDataSetChanged () को कॉल किया?
डेनिस पाल्निट्स्की

1
बेशक, onProgressUpdate में अनुक्रम होता है: list.setVisibility (GONE) - addObjectToList / सूची पर सिंक्रनाइज़ ऑपरेशन / - InformDataSetChanged () - list.setVetibility (VISIBLE) (और दृश्यता संशोधनों के बिना अपवाद अधिक बार होता है)
tomash

1
क्या आप अंतर्निहित ArrayList को एक अलग थ्रेड पर संशोधित कर रहे हैं? सभी परिवर्तन, यहां तक ​​कि एडाप्टर द्वारा संदर्भित ArrayList को भी UI थ्रेड पर होना है।
रिच शासक

2
@ क्यूबर्टिकस - जैसा कि मैंने स्पष्ट रूप से कहा है, मैं अलग-अलग धागे से एरेलेस्टिस्ट को संशोधित नहीं करता हूं, लेकिन एक्यूंस्कैट्स के ऑनप्रोग्रेड यूपीडेट से - यह जीयूआई धागे में काम करता है।
टामैश

1
@tomash मुझे वही अपवाद मिला जो मैं ताज़ा करने के लिए पुल का उपयोग कर रहा हूं और पोस्ट निष्पादन पर मैंने एडेप्टर लिखा है। नोटीसैटसैचेंज (); lvAutherlist.completeRefreshing (); लेकिन कभी-कभी यह त्रुटि मिली कि इसे कैसे हल किया जाए
खान

जवाबों:


119

मेरी भी यही समस्या थी।

मैं ArrayListUI थ्रेड के बाहर अपने आइटम जोड़ रहा था ।

समाधान: मैंने दोनों किया है, adding the itemsऔर notifyDataSetChanged()यूआई थ्रेड में बुलाया है।


4
मैंने दोनों आइटमों को जोड़ा और UI थ्रेड में InformDataSetChanged () कहा और मैंने इसे हल किया।
मुलिन्स

23
वास्तविक टिप्पणी, वास्तव में।
डेंटेक्स

2
यह एक बहुस्तरीय मुद्दा है और उचित रूप से सिंक्रोनाइज़्ड ब्लॉक्स का उपयोग करके इसे रोका जा सकता है। UI थ्रेड पर अतिरिक्त चीजें डाले बिना और एप्लिकेशन की जवाबदेही के नुकसान के कारण। नीचे मेरा जवाब चेक करें।
जवांटर

2
भविष्य के पाठकों के लिए - बैकग्राउंड थ्रेड में कैलकुलेशन करना एक अच्छी बात है, लेकिन आपको यूआई थ्रेड में परिणाम पास करने चाहिए और एडॉप्टर बेस कलेक्शन में एडिटिंग आइटम्स को करना चाहिए और कोड के एक ही ब्लॉक में एडॉप्टर को सूचित करना चाहिए।
टामैश

4
@ मुलिंस यू यू कृपया अपने समाधान के लिए एक नमूना दिखा सकते हैं? मैं एक ही मुद्दे पर अटक गया हूं
जस

27

मुझे वही समस्या थी, लेकिन मैंने इसे विधि का उपयोग करके तय किया

requestLayout();

वर्ग से ListView


35
हमें कब इस विधि को कॉल करना चाहिए?
JehandadK

3
@DeBuGGeR के बाद आप या तो ListView में आइटम जोड़ते हैं या इसके एडेप्टर को बदलते हैं।
अहमत नोयान किज़िल्टन

क्या होगा अगर मैं एसिंक्टस्क में तत्वों को जोड़ रहा हूं
डॉ। एनड्रो

2
बस रिकॉर्ड के लिए, @ DraNdRO, आप अपने परिणाम AsyncTask में एकत्र करते हैं। doInBackground () और AsyncTask.onPostExecute () में सूची को अपडेट करने के लिए अपने परिणाम सहेजें, जो UI थ्रेड में चलेगा। आप अगर जरूरत के रूप में आप, उपयोग AsyncTask.publishProgress () जाने के लिए और AsyncTask.onProgressUpdate () जो भी यूआई धागा में चलाता है अद्यतन करने के लिए।
निकोल बोरेल्ली

21

यह एक मल्टीट्रेडिंग इश्यू है और उचित रूप से सिंक्रोनाइज़ किया गया है ब्लॉक्स इसे रोका जा सकता है। यूआई थ्रेड पर अतिरिक्त चीजें डाले बिना और ऐप की जवाबदेही का नुकसान।

मैंने भी उसी का सामना किया। और सबसे स्वीकार्य उत्तर के रूप में यूआई थ्रेड से एडॉप्टर डेटा में बदलाव करने से समस्या का समाधान हो सकता है। यह काम करेगा लेकिन एक त्वरित और आसान समाधान है लेकिन सबसे अच्छा नहीं है।

जैसा कि आप एक सामान्य मामले के लिए देख सकते हैं। बैकग्राउंड थ्रेड से डेटा एडेप्टर को अपडेट करना और यूआई थ्रेड कार्यों में सूचनाडॉटसेटचैंग को कॉल करना।

जब कोई ui थ्रेड दृश्य को अपडेट कर रहा होता है और एक अन्य बैकग्राउंड थ्रेड डेटा को फिर से बदलता है तो यह गैरकानूनी तरीके से उत्पन्न होता है। यही क्षण इस समस्या का कारण बनता है।

इसलिए यदि आप सभी कोड को सिंक्रनाइज़ करेंगे जो एडेप्टर डेटा को बदल रहा है और नोटिफेडसेटकचेंज कॉल कर रहा है। यह मुद्दा जाना चाहिए। मेरे लिए और मैं अभी भी पृष्ठभूमि थ्रेड से डेटा को अपडेट कर रहा हूं।

दूसरों को संदर्भित करने के लिए यहां मेरा मामला विशिष्ट कोड है।

मुख्य स्क्रीन पर मेरा लोडर पृष्ठभूमि में मेरे डेटा स्रोतों में फोन बुक संपर्कों को लोड करता है।

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

यह PhoneBookManager.getPhoneBookContacts फोनबुक से संपर्क पढ़ता है और उन्हें हैशमैप में भरता है। सूची आकर्षित करने के लिए सूची अनुकूलन के लिए सीधे प्रयोग करने योग्य है।

मेरी स्क्रीन पर एक बटन है। यह एक गतिविधि खोलता है जहां ये फ़ोन नंबर सूचीबद्ध हैं। यदि मैं सीधे सूची पर सेट करता हूं तो पिछले थ्रेड से पहले काम खत्म हो जाता है जो कि फास्ट नेवीगेशन केस है जो अक्सर कम होता है। यह अपवाद को दर्शाता है। जो इस SO प्रश्न का शीर्षक है। इसलिए मुझे दूसरी गतिविधि में ऐसा कुछ करना होगा।

दूसरी गतिविधि में मेरा लोडर पहले धागे के पूरा होने की प्रतीक्षा करता है। तक यह एक प्रगति बार दिखाता है। दोनों लोडर के loadInBackground की जाँच करें।

फिर यह एडेप्टर बनाता है और इसे उस गतिविधि तक पहुँचाता है जहाँ ui थ्रेड पर मैं कॉल करता हूँ।

इससे मेरी समस्या हल हो गई।

यह कोड केवल एक स्निपेट है। आपको इसे आपके लिए अच्छी तरह से संकलित करने के लिए बदलने की आवश्यकता है।

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

}

उम्मीद है की यह मदद करेगा


क्षमा करें, आपने ऐसा कैसे किया? आपने पृष्ठभूमि थ्रेड कोड और Informdatasetchange कॉल को कैसे सिंक्रनाइज़ किया? मैं एक स्वत: पूर्ण टेक्स्ट दृश्य पर डेटा फ़िल्टर करते समय एक doInBackground का उपयोग कर रहा हूं और मैं एक ही समस्या का सामना कर रहा हूं ... क्या आप मुझे हल करने के लिए कुछ सलाह दे सकते हैं?
टोनिक्स

@ user3019105 - दो पृष्ठभूमि थ्रेड को सिंक्रनाइज़ करना। एक जो बैकग्राउंड में डेटा अपडेट करता है और दूसरा जो ui थ्रेड को सेट करने के लिए सूचित करता है। AdAdadpter या Informdatasetchanged को उपलब्ध हैंडलर.पोस्ट विधियों या runOnUiThread विधियों का उपयोग करके। मेरे विशेष मामले के लिए मैंने दो लोडर को अपने doInBackground के साथ एक सिंगलटन ऑब्जेक्ट पर सिंक्रनाइज़ किया। डैशबोर्ड स्क्रीन के लिए एक जल्द ही स्प्लैश स्क्रीन पर डेटा तैयार करना शुरू हो जाता है। वह मेरी जरूरत थी।
जवाँतेर

क्या आप कृपया इस कार्यान्वयन के कोड का एक स्निपेट पोस्ट कर सकते हैं? मेरे मामले में, मैंने एडॉप्टर पर कॉलिंग ऑब्जेक्ट्स का एक अस्थायी ArrayList <T> बनाने, फिर एडऑल (tmpArrayList) और अंत में InformDataSetShanged () को कॉल करके स्पष्ट () कॉलिंग क्लियर () को हल किया है। मैं यह सब कॉल publishResult () फ़िल्टर विधि के अंदर करता हूं, जो मुख्य थ्रेड पर चल रहा है। क्या आपको लगता है कि यह एक विश्वसनीय समाधान है?
टोनिक्स

@ user3019105 संपादित उत्तर की जाँच करें। आशा है कि अब यह आपके लिए मददगार होगा
जावेंटर

इस जवाब के लिए बहुत सारे पढ़ने की आवश्यकता है कि यह क्यों काम करता है, tutorials.jenkov.com/java-concurrency/synchronized.html शुरू करने के लिए एक अच्छी जगह है
abdu

15

मैंने इसे 2 सूचियों द्वारा हल किया है। एक सूची जिसे मैं केवल एडॉप्टर के लिए उपयोग करता हूं, और मैं अन्य सूची में सभी डेटा परिवर्तन / अपडेट करता हूं। यह मुझे पृष्ठभूमि थ्रेड में एक सूची पर अपडेट करने की अनुमति देता है, और फिर मुख्य / 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 नहीं मिली थी। मैं एंड्रॉइड फ्रेमवर्क को इस पर संदेह का लाभ देने जा रहा हूं और कहता हूं कि यह आपके कोड में सबसे अधिक त्रुटि है। आशा है कि ये आपकी मदद करेगा। हो सकता है कि आप इसे अपनी सूची और डेटा में अनुकूलित कर सकें।

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; मेरा कार्यान्वयन सही निकला, जो कि बिल्कुल सही नहीं था, क्योंकि हर नोटिफ़ाइडसेट से पहले छंटनी की गई थी ()। मैं इसे आज़माऊँगा - अगर दुर्घटनाएँ बच जाती हैं, तो मैं जाँच जारी रखूँगा। सादर!
टामैश

4

कई दिनों पहले मैं एक ही समस्या से जुड़ा था और प्रति दिन कई हजारों दुर्घटना का कारण बनता है, लगभग 0.1% उपयोगकर्ता इस स्थिति से मिलते हैं। मैंने कोशिश की setVisibility(GONE/VISIBLE)और requestLayout(), लेकिन क्रैश गणना केवल थोड़ी घट जाती है।

और मैंने आखिरकार इसे हल कर दिया। साथ कुछ नहीं setVisibility(GONE/VISIBLE)। साथ कुछ नहीं requestLayout()

अंत में मुझे यह कारण मिला कि मैंने अपडेट डेटा के बाद Handlerकॉल notifyDataSetChanged()करने के लिए एक का उपयोग किया है , जिससे एक प्रकार हो सकता है:

  1. मॉडल ऑब्जेक्ट में डेटा अपडेट करता है (मैं इसे डेटा स्रोत कहता हूं)
  2. उपयोगकर्ता सूची को छूता है (जो कॉल checkForTap()/ onTouchEvent()और अंत में कॉल कर सकता है layoutChildren())
  3. एडॉप्टर को मॉडल ऑब्जेक्ट और कॉल notifyDataSetChanged()और अपडेट विचारों से डेटा मिलता है

और मैंने एक और गलती की है कि getCount(), getItem()और getView(), मैं सीधे एडॉप्टर में कॉपी करने के बजाय डेटा स्रोत में फ़ील्ड्स का उपयोग करता हूं। तो आखिरकार यह दुर्घटनाग्रस्त हो जाता है जब:

  1. एडाप्टर डेटा को अपडेट करता है जो अंतिम प्रतिक्रिया देता है
  2. जब अगली प्रतिक्रिया वापस आती है, तो DataSource डेटा को अपडेट करता है, जो आइटम गणना परिवर्तन का कारण बनता है
  3. उपयोगकर्ता सूची को छूता है, जो एक टैप या एक चाल या फ्लिप हो सकता है
  4. getCount()और getView()कहा जाता है, और सूची का पता चलता है कि डेटा सुसंगत नहीं है, और जैसे अपवादों को फेंकता है java.lang.IllegalStateException: The content of the adapter has changed but...IndexOutOfBoundExceptionयदि आप शीर्ष लेख / पाद लेख का उपयोग करते हैं तो एक और सामान्य अपवाद है ListView

तो समाधान आसान है, मैं सिर्फ अपने डेटा स्रोत से एडेप्टर में डेटा की प्रतिलिपि बनाता हूं जब मेरा हैंडलर डेटा और कॉल प्राप्त करने के लिए एडेप्टर चलाता है notifyDataSetChanged()। अब दुर्घटना फिर कभी नहीं होती।


1
यह मेरा मुद्दा था। मेरे मामले में getCount () अधिसूचितडाटाचेंज को कॉल करने से पहले उपलब्ध नई गणना लौटा रहा था। चूँकि मेरी सूची 'आभासी' थी, इसलिए अब मैं अपनी नोटिफ़ाइडडैट विधि में अद्यतन गणना को कैप्चर करता हूँ और फिर गेटकाउंट () के माध्यम से गिनती के लिए पूछे जाने पर इस कैश्ड गिनती को वापस करता हूँ।
ग्लेन

3

यदि यह रुक-रुक कर होता था, तो मुझे केवल यही मुद्दा आता था जब सूची को एक 'लोड अधिक' अंतिम आइटम क्लिक करने के बाद स्क्रॉल किया गया था। यदि सूची को स्क्रॉल नहीं किया गया था, तो सब कुछ ठीक चला।

MUCH डिबगिंग के बाद, यह मेरी ओर से एक बग था, लेकिन एंड्रॉइड कोड में एक असंगतता भी थी।

जब सत्यापन होता है, तो इस कोड को 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();

जिस तरह से एडेप्टर की गारंटी नहीं है उसी तरह से ध्यान दें!

मेरे मामले में, चूंकि यह एक 'लोडमोर एडेप्टर' था, इसलिए मैं गेट एडेप्टर कॉल (अंतर्निहित वस्तुओं तक पहुंच के लिए) में रैप्डएड एडाप्टर लौटा रहा था। इसका परिणाम यह हुआ कि अतिरिक्त 'लोड मोर' आइटम और एक्ससेप्शन को फेंके जाने के कारण मायने रखता है।

मैंने केवल यही किया क्योंकि डॉक्स ऐसा लगता है कि ऐसा करना ठीक है

ListView.getAdapter javadoc

वर्तमान में इस सूची दृश्य में उपयोग किए जाने वाले एडाप्टर को लौटाता है। लौटा हुआ एडप्टर एक ही अडैप्टर नहीं हो सकता है जो कि AdAdapter (ListAdapter) को दिया गया हो लेकिन WrapperListAdapter हो सकता है।


मेरी भी इसी प्रकार की समस्या है । क्या आप कृपया इसे ठीक करने का सुझाव दे सकते हैं।
May13ank

यदि आप ऊपर दिए गए 2 कोड नमूनों को देखते हैं, तो आप ListView में mAdapter और ListView के एक अभिभावक में AdAdapter () देखें। यदि आप getAdapter () को ओवरराइड करते हैं, तो getAdapter () का परिणाम mAdapter नहीं हो सकता है। यदि 2 एडेप्टर की संख्या अलग है, तो आपको त्रुटि मिलती है।
अनारोन्वार्गस

3

मेरी समस्या सूची दृश्य के साथ फ़िल्टर के उपयोग से संबंधित थी ।

सूची दृश्य के अंतर्निहित डेटा मॉडल को सेट या अपडेट करते समय, मैं इस तरह से कुछ कर रहा था:

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

filter()अंतिम पंक्ति में कॉलिंग (और होनी चाहिए) notifyDataSetChanged()को फ़िल्टर publishResults()विधि में कहा जाता है । यह कभी-कभी ठीक काम कर सकता है, विशेष रूप से मेरे फास्ट नेक्सस 5 में। लेकिन वास्तव में, यह एक बग छिपा रहा है जिसे आप धीमे उपकरणों या संसाधन गहन परिस्थितियों में देखेंगे।

समस्या यह है कि फ़िल्टरिंग को असिंक्रोनस रूप से किया जाता है, और इस प्रकार filter()स्टेटमेंट और कॉल टू के बीच publishResults(), दोनों यूआई थ्रेड में, कुछ अन्य यूआई थ्रेड कोड एडाप्टर की सामग्री को निष्पादित और बदल सकते हैं।

वास्तविक निर्धारण आसान है, notifyDataSetChanged()फ़िल्टरिंग के अनुरोध के पहले भी कॉल करें :

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

3

मेरे पास एक सूची है यदि फ़ीड ऑब्जेक्ट। यह बिना यूआई-थ्रेड के एपेंडेड और ट्रेंकुलेटेड है। यह नीचे एडाप्टर के साथ ठीक काम करता है। मैं FeedAdapter.notifyDataSetChangedवैसे भी UI थ्रेड में कॉल करता हूं, लेकिन बाद में बहुत कम। मुझे यह पसंद है क्योंकि यूआई के मृत होने पर भी मेरी फ़ीड ऑब्जेक्ट स्थानीय सेवा में स्मृति में रहती हैं।

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 के कम संवेदनशील होने से बचने के लिए मैंने दूसरी बार 4 बार mAdapter.setNotifyOnChange(false)कॉल किया mAdapter.notifyDataSetChanged()। एक बार प्रति सेकंड सरणी को क्रमबद्ध किया जाता है।

यह अच्छी तरह से काम करता है और बहुत नशे की लत लगता है, लेकिन दुर्भाग्य से अक्सर दिखाए गए सूची आइटमों को छूकर इसे दुर्घटनाग्रस्त करना संभव है।

लेकिन ऐसा लगता है कि मुझे एक स्वीकार्य समाधान मिल गया है। मेरा अनुमान है कि भले ही आप यूआई थ्रेड पर काम करते हों लेकिन एडेप्टर बिना कॉल किए डेटा में कई बदलावों को स्वीकार नहीं करता है notifyDataSetChanged(), इस वजह से मैंने एक कतार बनाई है जो उल्लेखित 300ms के खत्म होने तक सभी नए आइटमों को संग्रहीत कर रही है। यदि यह क्षण पूरा हो जाता है तो मैं एक शॉट और कॉल में सभी संग्रहीत वस्तुओं को जोड़ता हूं notifyDataSetChanged()। अब तक मैं सूची को क्रैश करने में सक्षम नहीं था ।



2

यहां तक ​​कि मुझे अपने एक्सएमपीपी अधिसूचना आवेदन में भी इसी समस्या का सामना करना पड़ा, रिसीवर के संदेश को सूची दृश्य (साथ लागू ArrayList) में वापस जोड़ने की आवश्यकता है । जब मैंने रिसीवर सामग्री को जोड़ने की कोशिश कीMessageListener (अलग थ्रेड) के , तो एप्लिकेशन उपरोक्त त्रुटि के साथ समाप्त हो गया। मैंने इसे अपने arraylist& के setListviewadapaterमाध्यम से सामग्री को जोड़कर हल किया runOnUiThreadजो गतिविधि वर्ग का हिस्सा है। इससे मेरी समस्या हल हो गई।


1

मुझे इसी तरह की समस्या का सामना करना पड़ा, यहाँ मैंने अपने मामले में कैसे हल किया। मैं सत्यापित करता हूं कि क्या taskपहले से है RUNNINGया FINISHEDक्योंकि कोई कार्य केवल एक बार चल सकता है। नीचे आपको मेरे समाधान से एक आंशिक और अनुकूलित कोड दिखाई देगा।

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 थ्रेड पर नहीं चल रही है और EVENTUALLY यह कुछ समस्याओं को उठाती है।


1

इस दुर्घटना का एक कारण यह है कि ArrayListवस्तु पूरी तरह से बदल नहीं सकती है। इसलिए, जब मैं किसी आइटम को हटाता हूं, तो मुझे यह करना होगा:

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

इसने मेरे लिए दुर्घटना को ठीक कर दिया।


1

मेरे मामले में मैंने मुख्य गतिविधि पर विधि GetFilter()से एक एडॉप्टर पर विधि को बुलाया TextWatcher(), और मैंने डेटा को फॉर लूप के साथ जोड़ा GetFilter()। समाधान AfterTextChanged()मुख्य गतिविधि पर उप विधि के लिए लूप को बदल दिया गया और कॉल को हटा दिया गयाGetFilter()


1
कृपया अपना समाधान स्पष्ट करें। I`m भी स्थिति में u /
nAkhmedov

1
adapter.notifyDataSetChanged()

2
यह स्टैक ओवरफ्लो पर एक अच्छा अभ्यास है ताकि यह समझा जाए कि आपके समाधान को काम करना चाहिए या मौजूदा समाधानों से बेहतर है। अधिक जानकारी के लिए उत्तर कैसे पढ़ें ।
सैमुअल एलवाईई

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

                }

            });
        }

अब मेरा ऐप काम करता है।

संपादित करें: वास्तव में, मेरा ऐप अभी भी हर 1 से 10 बार दुर्घटनाग्रस्त हो गया, वही त्रुटि।

आखिरकार मैं runOnUiThreadएक पिछली पोस्ट पर आया , जो मुझे लगा कि उपयोगी हो सकता है। तो मैंने इसे अपने doInBackground पद्धति में रखा, जैसे:

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

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

और मैंने adapter.notifyDataSetChanged();विधि निकाल दी । अब, मेरा ऐप कभी क्रैश नहीं होता है।


0

कृपया इनमें से कोई एक समाधान आज़माएं:

  1. कभी-कभी, यदि आप थ्रेड (या doInBackgroundविधि) में डेटा सूची में नई ऑब्जेक्ट जोड़ते हैं, तो यह त्रुटि होगी। समाधान है: एक अस्थायी सूची बनाएं और थ्रेड (या doInBackground) में इस सूची में डेटा जोड़ते हैं , फिर यूआई थ्रेड (या onPostExcute) में अस्थायी सूची से एडाप्टर की सूची में सभी डेटा की नकल करते हैं

  2. सुनिश्चित करें कि सभी UI अपडेट को UI थ्रेड में कहा जाता है।


0

मैं एक ही समस्या है जब आलसी छवि लोडर में नया डेटा जोड़ मैं बस डाल दिया

         adapter.notifyDataSetChanged();

में

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

आशा है कि यह आपकी मदद करता है


0

जैसे @ मुलिंस ने कहा "
मैंने दोनों वस्तुओं को जोड़ा और notifyDataSetChanged()यूआई थ्रेड में बुलाया और मैंने इसे हल किया। - मुलिंस"।

मेरे मामले में मेरे पास है asynctaskऔर मैंने विधि notifyDataSetChanged()में फोन किया doInBackground()और समस्या हल हो गई, जब मुझे बुलाया गया तो onPostExecute()मुझे अपवाद मिला।


0

मेरे पास एक प्रथा थी ListAdapterऔर super.notifyDataSetChanged()शुरुआत में बुला रही थी न कि विधि के अंत में

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

0

मेरे पास समान सिचुएशन थी, मैंने कई बटोंगग्रुप को अपने आइटम को लिस्टव्यू पर रखा था और मैं धारक के अंदर कुछ बूलियन मान बदल रहा था जैसे कि धारक.rbVar.setOnclik ...

मेरी समस्या इसलिए हुई क्योंकि मैं getView () के अंदर एक विधि कह रहा था; और साझाकरण के अंदर किसी ऑब्जेक्ट को सहेज रहा था, इसलिए मुझे ऊपर एक ही त्रुटि थी

मैंने इसे कैसे हल किया; मैंने अपना तरीका getView () के अंदर अधिसूचितडेटासेट इनवाइटिड () और समस्या को हटा दिया

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

0

मुझे भी यही समस्या थी। आखिरकार मुझे समाधान मिल गया

सूची को अद्यतन करने से पहले, यदि सॉफ्ट कीपैड पहले मौजूद है तो उसे बंद कर दें। उसके बाद डेटा स्रोत सेट करें और कॉल करें Informdatasetchanged ()।

कीपैड को बंद करते समय आंतरिक रूप से सूची दृश्य अपने 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 row fetch, ...) और आइटम जोड़ें temp arraylist।

3) अस्थायी सूची से सभी वस्तुओं को अपनी सूची के सरणी सूची में जोड़ें onPostExecute

note:आप सूची से कुछ आइटम हटाना चाहते हैं और साइक्लाइट डेटाबेस से भी हटा सकते हैं और शायद sdcard से आइटम से संबंधित कुछ फ़ाइलों को हटा सकते हैं, बस डेटाबेस से आइटम हटा दें और उनकी संबंधित फ़ाइलों को हटा दें और उन्हें temp arraylist में जोड़ें background thread। फिरUI thread सरणी सूची से अस्थायी सरणी सूची में मौजूद आइटम हटाएं।

उम्मीद है की यह मदद करेगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.