অ্যান্ড্রয়েড, লিস্টভিউ অবৈধ স্টেট এক্সেপশন: "অ্যাডাপ্টারের বিষয়বস্তু পরিবর্তন হয়েছে তবে তালিকাভিউ একটি বিজ্ঞপ্তি পায়নি"


188

আমি কী করতে চাই : একটি ব্যাকগ্রাউন্ড থ্রেড চালান যা তালিকাগুলির বিষয়বস্তু গণনা করে এবং তালিকাভিউকে আংশিকভাবে আপডেট করে, ফলাফলগুলি গণনা করা হয়।

আমি যা জানি তা এড়াতে হবে : আমি ব্যাকগ্রাউন্ড থ্রেড থেকে লিস্টএডাপ্টার সামগ্রীগুলিতে গণ্ডগোল করতে পারি না, সুতরাং আমি অ্যাসিঙ্কটাস্ক উত্তরাধিকার সূত্রে পেয়েছি এবং অনপ্রসেসআপডেট থেকে ফলাফল (অ্যাডাপ্টারে এন্ট্রি যুক্ত করে) প্রকাশ করি। আমার অ্যাডাপ্টার ফলাফল অবজেক্টগুলির অ্যারেলিস্ট ব্যবহার করে, সেই অ্যারেলিস্টগুলিতে সমস্ত ক্রিয়াকলাপ সিঙ্ক্রোনাইজ করা হয়।

অন্যান্য লোকের গবেষণা : এখানে খুব মূল্যবান তথ্য রয়েছে । আমি প্রায় 500 list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)ডলারের ব্যবহারকারীর গ্রুপের জন্য প্রায় দৈনিক ক্র্যাশের শিকার হয়েছিলাম এবং আমি যখন প্রপ্রেসআপেডে ব্লক যুক্ত করেছিলাম তখন 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
আপনি কি notifyDataSetChanged () কল করেছেন?
ডেনিস Palnitsky

1
অবশ্যই, প্রোগ্রেসআপটেডে ধারাবাহিকতা রয়েছে: list.setVisibility (GONE) - addObjectToList / তালিকাটিতে সিঙ্ক্রোনাইজড অপারেশন - notifyDataSetChanged () - list.setVisibility (VISIBLE) (এবং দৃশ্যমান পরিবর্তন ব্যতীত আরও অনেক বেশি ঘটে)
টমাস

1
আপনি কি অন্য থ্রেডের অন্তর্নিহিত অ্যারেলিস্টটি পরিবর্তন করছেন? অ্যাডাপ্টার দ্বারা রেফারেন্সযুক্ত অ্যারেলিস্টে সমস্ত পরিবর্তনগুলি ইউআই থ্রেডে ঘটতে হবে।
ধনী শুলার

2
@ কিবার্টিকাস - যেমন আমি স্পষ্ট করে বলেছি, আমি অ্যারেলিস্টকে বিভিন্ন থ্রেড থেকে পরিবর্তন করি না, তবে একাইঙ্কটাস্কের প্রপ্রেসআপডেট পদ্ধতি থেকে - এটি জিইউআই থ্রেডে কাজ করে।
টমাশ

1
@ টমশ আমি রিফ্রেশ করতে টান ব্যবহার করছি এবং পোস্ট এক্সিকিউটে আমি অ্যাডাপ্টার লিখেছি not নোটডেডিটাসেট চেঞ্জড (); lvAutherlist.completeRefreshing (); তবে কখনও কখনও কীভাবে সমাধান করা যায় এই ত্রুটিটি পেয়েছিলেন
খান

উত্তর:


119

আমারো একই ইস্যু ছিল.

আমি আমার ArrayListইউআই থ্রেডের বাইরে আইটেম যুক্ত করছি ।

সমাধান: আমি উভয়ই করেছি adding the itemsএবং notifyDataSetChanged()ইউআই থ্রেডে ডেকেছি ।


4
আমি দুজনেই আইটেম যুক্ত করেছি এবং ইউআই থ্রেডে notifyDataSetChanged () বলেছি এবং আমি এটি সমাধান করেছি।
মুলিনস

23
জেনিয়াল মন্তব্য, সত্যিই।
dentex

2
এটি একটি মাল্টিথ্রেডিং ইস্যু এবং সঠিকভাবে সিঙ্ক্রোনাইজড ব্লক ব্যবহার করা এটি প্রতিরোধ করা যেতে পারে। ইউআই থ্রেডে অতিরিক্ত জিনিস না রেখে এবং অ্যাপের প্রতিক্রিয়াহীনতার ক্ষতি না করে my নীচে আমার উত্তরটি দেখুন।
জাভানেটর

2
ভবিষ্যতের পাঠকদের জন্য - ব্যাকগ্রাউন্ড থ্রেডে গণনা করা ভাল জিনিস তবে আপনার ইউআই থ্রেডে ফলাফলগুলি পাস করা উচিত এবং কোডের একই ব্লকে অ্যাডাপ্টারের বেস সংগ্রহ এবং অ্যাডাপ্টারকে বিজ্ঞাপিত করা উচিত।
টমশ

4
@ মুলিনস আপনি কি দয়া করে আপনার সমাধানের জন্য একটি নমুনা দেখাতে পারবেন? আমি একই ইস্যুতে আটকে আছি
জাস

27

আমার একই সমস্যা ছিল, তবে আমি পদ্ধতিটি ব্যবহার করে এটি ঠিক করেছি

requestLayout();

ক্লাস থেকে ListView


35
আমাদের কখন এই পদ্ধতিটি কল করা উচিত?
জাহান্দেকে

3
আপনি ডেডুগিজিআর আপনার পরে হয় তালিকার ভিউতে আইটেম যুক্ত করুন বা এর অ্যাডাপ্টার পরিবর্তন করুন।
আহমেট নওয়ান কোজল্টান

আমি যদি অ্যাসিঙ্কটাসে উপাদান যুক্ত করছি তবে কী হবে
ডাঃ এএনড্রো '

2
কেবলমাত্র ডক্টরএনডিআরও রেকর্ডের জন্য আপনি অ্যাসিঙ্কটাস্কে আপনার ফলাফল সংগ্রহ করেছেন collect doInBackground () করুন এবং AsyncTask.onPostExecute () এ তালিকা আপডেট করতে আপনার ফলাফলগুলি সংরক্ষণ করুন, যা ইউআই থ্রেডে চলবে। আপনি যদি প্রয়োজন হিসাবে আপনি, ব্যবহার AsyncTask.publishProgress () যান এবং AsyncTask.onProgressUpdate () যা UI 'তে থ্রেড রান আপডেটে।
নিকোল বোরেলি

21

এটি একটি মাল্টিথ্রেডিং ইস্যু এবং সঠিকভাবে সিঙ্ক্রোনাইজড ব্লক ব্যবহার করা এটি প্রতিরোধ করা যেতে পারে। ইউআই থ্রেডে অতিরিক্ত জিনিস না রেখে এবং অ্যাপ্লিকেশনটির প্রতিক্রিয়া হারাতে না পারে।

আমিও এর মুখোমুখি হয়েছি। এবং সর্বাধিক গৃহীত উত্তর হিসাবে ইউআই থ্রেড থেকে অ্যাডাপ্টারের ডেটা পরিবর্তন করার পরামর্শ দেয় এটি সমস্যার সমাধান করতে পারে। এটি কাজ করবে তবে একটি দ্রুত এবং সহজ সমাধান তবে সেরাটি নয়।

আপনি যেমন একটি সাধারণ ক্ষেত্রে দেখতে পারেন। ব্যাকগ্রাউন্ড থ্রেড থেকে ডেটা অ্যাডাপ্টার আপডেট করা এবং UI থ্রেডে notifyDataSetChanged কল করা কাজ করে।

যখন কোনও ইউআই থ্রেড ভিউ আপডেট করে থাকে এবং অন্য একটি পটভূমি থ্রেড আবার ডেটা পরিবর্তন করে This এই মুহূর্তটি এই সমস্যার কারণ হয়ে দাঁড়ায়।

সুতরাং আপনি যদি সমস্ত কোড সিঙ্ক্রোনাইজ করে যা অ্যাডাপ্টারের ডেটা পরিবর্তন করে এবং নোটিফাইডট্যাসেটেঞ্জিং কল করছে। এই সমস্যাটি চলে যাওয়া উচিত। আমার জন্য যেমন গেছে এবং আমি এখনও পটভূমির থ্রেড থেকে ডেটা আপডেট করছি।

অন্যদের উল্লেখ করার জন্য এখানে আমার কেস নির্দিষ্ট কোড।

মূল স্ক্রিনে আমার লোডার পটভূমিতে আমার ডেটা উত্সগুলিতে ফোন বইয়ের পরিচিতিগুলি লোড করে।

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

এই ফোনবুকমনেজ.জেটফোনবুকস যোগাযোগগুলি ফোনবুক থেকে পরিচিতি পড়ে এবং হ্যাশম্যাপে পূরণ করে। যা তালিকা আঁকার জন্য তালিকা অ্যাডাপ্টারগুলির জন্য সরাসরি ব্যবহারযোগ্য।

আমার স্ক্রিনে একটি বোতাম আছে। এই ক্রিয়াকলাপটি খুলবে যেখানে এই ফোন নম্বরগুলি তালিকাবদ্ধ রয়েছে। পূর্ববর্তী থ্রেডটি কাজ শেষ করার আগে যদি আমি সরাসরি তালিকার উপরে অ্যাডাপ্টার সেট করে রাখি যা দ্রুত ন্যাভিগেশন কেসটি প্রায়শই ঘটে। এটি ব্যতিক্রমকে পপ আপ করে দেয় thisএই প্রশ্নটির শিরোনাম। সুতরাং দ্বিতীয় ক্রিয়াকলাপে আমাকে এরকম কিছু করতে হবে।

দ্বিতীয় ক্রিয়ায় আমার লোডার প্রথম থ্রেডটি সম্পূর্ণ হওয়ার জন্য অপেক্ষা করে। যতক্ষণ না এটি একটি অগ্রগতি বার দেখায়। উভয় লোডারের লোডইনব্যাকগ্রাউন্ড পরীক্ষা করুন।

তারপরে এটি অ্যাডাপ্টার তৈরি করে এবং ক্রিয়াকলাপে এটি সরবরাহ করে যেখানে ইউআই থ্রেডে আমি সেটএডাপ্টার কল করি।

এটি আমার সমস্যা সমাধান করেছে।

এই কোডটি কেবল একটি স্নিপেট। আপনার জন্য এটি ভাল সংকলন করার জন্য আপনাকে এটি পরিবর্তন করতে হবে।

@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 কলকে সিঙ্ক্রোনাইজ করলেন? অটোকম্পিউলিটেক্সটভিউতে ডেটা ফিল্টার করার সময় আমি একটি ডাইনব্যাকগ্রাউন্ড ব্যবহার করছি এবং আমি একই সমস্যার মুখোমুখি হচ্ছি ... সমাধানের জন্য আপনি কি আমাকে কিছু পরামর্শ দিতে পারেন?
টোনিক্স

@ ব্যবহারকারী 3019105 - দুটি ব্যাকগ্রাউন্ড থ্রেড সিঙ্ক্রোনাইজ করা। একটি যা ব্যাকগ্রাউন্ডে ডেটা আপডেট করে এবং অন্যটি যা হোল্ডারপোস্ট পদ্ধতিগুলি বা রানঅনউইথ্রেড পদ্ধতি উপলভ্য ব্যবহার করে ইউআই থ্রেডকে সেটএডপাস্টার বা নোটিফিকেশন ডেটাচেটে অবহিত করে। আমার বিশেষ ক্ষেত্রে আমি দুটি লোডারকে তাদের ডইনব্যাকগ্রাউন্ডের সাথে একটি সিঙ্গলটন অবজেক্টে সিঙ্ক্রোনাইজ করেছি। ড্যাশবোর্ড স্ক্রিনের জন্য দ্রুত উপলভ্য হওয়ার জন্য একবার স্প্ল্যাশ স্ক্রিনে ডেটা প্রস্তুত করা শুরু করুন। এটা আমার প্রয়োজন ছিল।
জাভানেটর

আপনি কি দয়া করে এই প্রয়োগের কোডের স্নিপেট পোস্ট করতে পারেন? আমার ক্ষেত্রে, আমি অ্যাডাপ্টারে ক্লিয়ার () কল করার সমাধান করেছি, ফিল্টার করা বস্তুর অস্থায়ী অ্যারেলিস্ট <T> তৈরি করেছি, তারপরে অ্যাডএল (tmpArrayList) এবং অবশেষে notifyDataSetChanged () এ কল করব। আমি এই সমস্ত কলগুলি মূল থ্রেডে চলমান প্রকাশের ফলাফল () ফিল্টার পদ্ধতির অভ্যন্তরে করি। আপনি কি এটি একটি নির্ভরযোগ্য সমাধান বলে মনে করেন?
টোনিক্স

@ ব্যবহারকারী 3019105 সম্পাদিত উত্তরটি দেখুন। আশা করি এটি এখন আপনার পক্ষে সহায়ক
জাভানেটর

এই উত্তরটি কেন এটি কাজ করে তা নিয়ে প্রচুর পড়া দরকার, টিউটোরিয়াল.
jenkov.com/java-concurrency/synchronized.html

15

আমি 2 টি তালিকা রেখে এটি সমাধান করেছি। একটি তালিকা আমি কেবল অ্যাডাপ্টারের জন্য ব্যবহার করি এবং আমি অন্য তালিকার সমস্ত ডেটা পরিবর্তন / আপডেট করি। এটি আমাকে পটভূমির থ্রেডের একটি তালিকায় আপডেট করার অনুমতি দেয় এবং তারপরে মূল / ইউআই থ্রেডে "অ্যাডাপ্টার" তালিকা আপডেট করে:

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

আমি এই কোডটি লিখেছিলাম এবং এটি ২.১ ঘন্টার জন্য একটি ২.১ এমুলেটর ইমেজে চালিত করেছিলাম এবং ইলিজালস্টেট এক্সেপশন পাইনি। আমি এ সম্পর্কিত সন্দেহের অ্যান্ড্রয়েড কাঠামোটি দিতে যাচ্ছি এবং বলব যে এটি সম্ভবত আপনার কোডের একটি ত্রুটি। আশা করি এটা কাজে লাগবে. হতে পারে আপনি এটিকে আপনার তালিকা এবং ডেটাতে মানিয়ে নিতে পারেন।

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

আপনার কাজের জন্য ধন্যবাদ! আমি লক্ষ্য করেছি যে অ্যারেএডাপ্টার পদ্ধতির জন্য hasStableIds () = মিথ্যা; আমার বাস্তবায়ন সত্যটি ফিরে আসল যা একেবারেই ঠিক ছিল না, কারণ প্রতিটি বিজ্ঞপ্তিডেটসেট চেঞ্জড () এর আগে সারিগুলি বাছাই করা হয়েছিল। আমি এটি দিয়ে চেষ্টা করব - যদি ক্র্যাশগুলি বেঁচে থাকে তবে আমি তদন্ত চালিয়ে যাব। শুভেচ্ছান্তে!
টমাশ

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. যখন পরবর্তী প্রতিক্রিয়া ফিরে আসবে, ডেটাসোর্স ডেটা আপডেট করে, যা আইটেমের গণনা পরিবর্তনের কারণ
  3. ব্যবহারকারী তালিকার স্পর্শ করে যা কোনও আলতো চাপ বা মুভ বা ফ্লিপ হতে পারে
  4. getCount()এবং getView()বলা হয়, এবং listview খুঁজে বের করে তথ্য সঙ্গতিপূর্ণ নয় এবং অন্য কোনো ব্যতিক্রম ছোঁড়ার java.lang.IllegalStateException: The content of the adapter has changed but...। আর একটি সাধারণ ব্যতিক্রম হ'ল IndexOutOfBoundExceptionযদি আপনি শিরোলেখ / পাদলেখ ব্যবহার করেন ListView

সুতরাং সমাধানটি সহজ, আমি আমার ডাটাসোর্স থেকে অ্যাডাপ্টারে ডেটা অনুলিপি করি যখন আমার হ্যান্ডলার ডেটা এবং কল পেতে অ্যাডাপ্টারের ট্রিগার করে notifyDataSetChanged()। ক্রাশ এখন আর কখনও ঘটে না।


1
এটা আমার সমস্যা ছিল। আমার ক্ষেত্রে getCount () notifyDataSetChanged বলা হওয়ার আগে উপলব্ধ নতুন গণনাটি ফিরিয়ে দিচ্ছিল। যেহেতু আমার তালিকাটি 'ভার্চুয়াল' ছিল, তাই আমি এখন আমার বিজ্ঞপ্তি ডেটাসেটচ্যাঞ্জড পদ্ধতিতে আপডেট গণনাটি ক্যাপচার করি এবং তারপরে getCount () এর মাধ্যমে গণনার জন্য জিজ্ঞাসা করা হলে এই ক্যাশেড কাউন্টটি ফিরিয়ে আনি।
গ্লেন

3

এটি যদি মাঝে মাঝে ঘটেছিল, সক্রিয় হয় কেবল তখনই আমার এই সমস্যাটি ঘটে যখন 'আরও বেশি লোড' শেষ আইটেমটি ক্লিক করার পরে তালিকাটি স্ক্রল করা হয়। যদি তালিকাটি স্ক্রোল না করা হয় তবে সবকিছু ঠিকঠাক কাজ করেছিল।

অনেকগুলি ডিবাগিংয়ের পরে, এটি আমার পক্ষ থেকে একটি বাগ ছিল, তবে অ্যান্ড্রয়েড কোডেও একটি অসঙ্গতি।

যখন বৈধতাটি ঘটে তখন এই কোডটি তালিকাভিউতে কার্যকর করা হয়

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

তবে যখন অন চেঞ্জ হয় এটি অ্যাডাপ্টারভিউতে এই তালিকাটি ছড়িয়ে দেয় (তালিকার ভিউরেন্ট)

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

অ্যাডাপ্টারের যেভাবে একই হওয়ার নিশ্চয়তা নেই তা লক্ষ্য করুন!

আমার ক্ষেত্রে, যেহেতু এটি 'লোডমোরএডাপ্টার' ছিল তাই আমি getAdapter কল (অন্তর্নিহিত অবজেক্টগুলিতে অ্যাক্সেসের জন্য) র্যাপড অ্যাডাপ্টারটি ফিরিয়ে দিচ্ছিলাম। অতিরিক্ত 'লোড মোর' আইটেম এবং ব্যতিক্রম নিক্ষেপের কারণে এটি গণনাগুলি পৃথক হতে পারে।

আমি কেবল এটি করেছি কারণ ডক্সগুলি দেখে মনে হয় এটি করা ঠিক আছে

লিস্টভিউ.জেটএডাপ্টার জাভাদোক

এই তালিকার দৃশ্যে বর্তমানে ব্যবহৃত অ্যাডাপ্টারটি ফিরিয়ে দেয়। প্রত্যাবর্তিত অ্যাডাপ্টার সেটএডাপ্টারে (লিস্টএডাপ্টার) পাস করা একই অ্যাডাপ্টার নাও হতে পারে তবে একটি র্যাপারলিস্ট অ্যাডাপ্টার হতে পারে।


আমারও একই সমস্যা হচ্ছে। আপনি কীভাবে এটি ঠিক করবেন পরামর্শ দিতে পারেন?
13ank 8

আপনি যদি উপরে 2 টি কোডের নমুনাগুলি দেখেন তবে আপনি তালিকাভিউতে (অ্যাডাপ্টারভিউ) পিতামাতার তালিকাতে এমএডাপ্টার এবং getAdapter () দেখতে পাবেন। আপনি getAdapter () কে ওভাররাইড করলে, getAdapter () এর ফলাফল এমএডাপ্টার নাও হতে পারে। যদি 2 অ্যাডাপ্টারের সংখ্যা পৃথক হয় তবে আপনি ত্রুটি পান।
অ্যারোনভার্গাস

3

আমার সমস্যা তালিকাভিউয়ের সাথে ফিল্টার ব্যবহারের সাথে সম্পর্কিত ছিল ।

তালিকাভিউর অন্তর্নিহিত ডেটা মডেলটি সেট বা আপডেট করার সময়, আমি এই জাতীয় কিছু করছি:

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

filter()শেষ লাইনে কল করার ফলে (এবং অবশ্যই) notifyDataSetChanged()ফিল্টার publishResults()পদ্ধতিতে কল করা হবে। এটি কখনও কখনও বিশেষত আমার দ্রুত নেক্সাস 5 এ ঠিকঠাক কাজ করতে পারে But তবে বাস্তবে, এটি এমন একটি বাগ লুকিয়েছে যা আপনি ধীর ডিভাইসগুলির সাথে বা রিসোর্স নিবিড় অবস্থাতে লক্ষ্য করবেন।

সমস্যাটি হ'ল ফিল্টারিংটি অবিচ্ছিন্নভাবে করা হয়, এবং এইভাবে filter()বিবৃতিটির শেষের দিকে এবং publishResults()উভয় ইউআই থ্রেডের মধ্যে, অন্য কোনও ইউআই থ্রেড কোড অ্যাডাপ্টারের সামগ্রী কার্যকর করতে এবং পরিবর্তন করতে পারে।

আসল ফিক্স সহজ, notifyDataSetChanged()ফিল্টারিং সম্পাদন করার জন্য অনুরোধ করার আগে কেবল কল করুন :

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

3

ফিডের জিনিসগুলি থাকলে আমার একটি তালিকা রয়েছে have এটি কোনও-ইউআই থ্রেড থেকে সংযুক্ত এবং ছাঁটা হয়েছে। এটি নীচে অ্যাডাপ্টারের সাথে সূক্ষ্মভাবে কাজ করে। আমি FeedAdapter.notifyDataSetChangedযেভাবেই হোক ইউআই থ্রেডে কল করি তবে কিছুটা পরে। আমি এটি পছন্দ করি কারণ আমার ফিডের অবজেক্টগুলি ইউআই মারা যাওয়ার পরেও লোকাল সার্ভিসে স্মৃতিতে থাকে।

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()অ্যাসিঙ্কটাস্ক অ্যাডাপ্টারের সাথে মানগুলি যুক্ত করে যুক্ত করে mAdapter.add(newEntry)। ইউআই কম প্রতিক্রিয়াশীল হওয়া এড়াতে আমি দ্বিতীয় বার 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আমি যে অ্যারেতে ডেটা রেখেছি তা নিয়ে গণ্ডগোল করছিলাম এবং সমস্যাটি হ'ল যেহেতু এই পদ্ধতিটি ইউআই থ্রেডটিতে চলছে না এবং অবিলম্বে এটি কিছু সমস্যা উত্থাপন করে।


1

এই ক্রাশের একটি কারণ হ'ল ArrayListঅবজেক্টটি পুরোপুরি পরিবর্তন করতে পারে না। সুতরাং, যখন আমি কোনও আইটেম সরিয়ে ফেলি, তখন আমাকে এটি করতে হবে:

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

এটি আমার জন্য ক্র্যাশ স্থির করেছে।


1

আমার ক্ষেত্রে আমি মূল ক্রিয়াকলাপের পদ্ধতি GetFilter()থেকে অ্যাডাপ্টারে TextWatcher()পদ্ধতিটি কল করেছি এবং আমি ফর লুপ অন দিয়ে ডেটা যুক্ত করেছি GetFilter()। সমাধানটি ছিল AfterTextChanged()মূল ক্রিয়াকলাপের জন্য লুপের সাব পদ্ধতিতে পরিবর্তন এবং কলটি মুছে ফেলাGetFilter()


1
দয়া করে আপনার সমাধানটি পরিষ্কার করুন।
আইএএমও

1
adapter.notifyDataSetChanged()

2
আপনার সমাধানটি কেন কাজ করা উচিত বা বিদ্যমান সমাধানগুলির চেয়ে ভাল সে সম্পর্কে একটি ব্যাখ্যা যুক্ত করতে স্ট্যাক ওভারফ্লোতে এটি ভাল অনুশীলন। আরও তথ্যের জন্য পড়ুন কিভাবে উত্তর দিতে হয়
স্যামুয়েল লিউ

0

আমি ঠিক একই ত্রুটি পেয়েছিলাম এবং অ্যাসিঙ্কটাস্ক ব্যবহার করছিলাম:

`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();আমার ইউআই থ্রেডের নীচে রেখে এটি সমাধান করেছি , এটি আমার পোস্ট অ্যাসেকট পদ্ধতিতে অ্যাসিঙ্কটাস্ক। এটার মত :

 protected void onPostExecute(Void aVoid) {

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

           adapter.notifyDataSetChanged();

                }

            });
        }

এখন আমার অ্যাপ কাজ করে।

সম্পাদনা: বাস্তবে, আমার অ্যাপ্লিকেশনটি এখনও 10 বারের মধ্যে প্রতি 1 ক্রাশ করেছে, একই ত্রুটি দিয়েছে।

শেষ পর্যন্ত আমি runOnUiThreadআগের পোস্টে এসে পৌঁছলাম , যা আমি ভেবেছিলাম দরকারী হতে পারে। সুতরাং আমি এটিকে আমার ডিনব্যাকগ্রাউন্ড পদ্ধতিতে রেখেছি:

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

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

এবং আমি adapter.notifyDataSetChanged();পদ্ধতিটি সরিয়েছি । এখন, আমার অ্যাপ কখনই ক্রাশ হয় না।


0

দয়া করে এই সমাধানগুলির মধ্যে একটি ব্যবহার করে দেখুন:

  1. কখনও কখনও, আপনি যদি কোনও থ্রেডে (বা doInBackgroundপদ্ধতি) ডেটা তালিকায় নতুন অবজেক্ট যুক্ত করেন তবে এই ত্রুটি ঘটবে। সমাধানটি হ'ল: একটি অস্থায়ী তালিকা তৈরি করুন এবং থ্রেডে (বা doInBackground) এই তালিকায় ডেটা যুক্ত করুন , তারপরে অস্থায়ী তালিকা থেকে সমস্ত তথ্য ইউআই থ্রেডের অ্যাডাপ্টারের তালিকায় অনুলিপি করুন (বা onPostExcute)

  2. সমস্ত ইউআই আপডেটগুলি ইউআই থ্রেডে ডাকা হয়েছে তা নিশ্চিত করুন।


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

আমার একই সিটিচুয়েশন ছিল, আমার অনেকগুলি বাটগ্রুপ ছিল আমার আইটেমকে তালিকাগুলিতে প্ররোচিত করছিল এবং আমি আমার আইটেমের মধ্যে হোল্ডার.আরবিভার.সেটঅনলিকের মতো কিছু বুলিয়ান মান পরিবর্তন করছিলাম ...

আমার সমস্যাটি দেখা দিয়েছে কারণ আমি getView () এর অভ্যন্তরে কোনও পদ্ধতি কল করেছিলাম; এবং ভাগের পছন্দের মধ্যে একটি অবজেক্ট সংরক্ষণ করছিলাম, সুতরাং আমার উপরে একই ত্রুটি ছিল

আমি কীভাবে এটি সমাধান করেছি; ডেটাসেটইনফায়ালাইটিড () এবং সমস্যাটি শেষ করতে আমি getView () এর ভিতরে আমার পদ্ধতিটি সরিয়ে ফেলেছি

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

0

আমারও একই সমস্যা ছিল অবশেষে আমি সমাধান পেয়েছি

তালিকাভিউ আপডেট করার আগে, নরম কীপ্যাড উপস্থিত থাকলে প্রথমে এটি বন্ধ করুন। এর পরে ডেটা উত্স সেট করুন এবং কল করুন notifydatasetchanged ()।

কীপ্যাড বন্ধ করার সময় অভ্যন্তরীণভাবে তালিকাগুলি তার ইউআই আপডেট করবে। এটি কীপ্যাড বন্ধ হওয়া অবধি কল করে রাখে। সেই সময় যদি ডেটা উত্স পরিবর্তন হয় তবে এই ব্যতিক্রমটি ছুঁড়ে ফেলবে। যদি ডেটাটিঅ্যাক্টিভিটি রেজাল্টে আপডেট হয় তবে একই ত্রুটির সম্ভাবনা রয়েছে।

 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 পদ্ধতিতে করুন এবং অস্থায়ী অ্যারেলিস্টে আইটেম যুক্ত করুন।

3) onPostExecuteপদ্ধতিতে আপনার তালিকার ভিউ অ্যারেলিস্টে অস্থায়ী অ্যারেলিস্ট থেকে সমস্ত আইটেম যুক্ত করুন ।

note:আপনি তালিকা ভিউ থেকে কিছু আইটেম মুছে ফেলতে এবং স্ক্লাইট ডাটাবেস থেকে মুছে ফেলতে এবং এসডকার্ড থেকে আইটেম সম্পর্কিত কিছু ফাইল মুছতে পারেন, কেবল ডাটাবেস থেকে আইটেমগুলি সরিয়ে তাদের সম্পর্কিত ফাইলগুলি সরিয়ে এবং টেম্পের অ্যারেলিস্টে যুক্ত করতে পারেন background thread। তারপরে UI threadতালিকাভুক্তির অ্যারেলিস্ট থেকে অস্থায়ী অ্যারেলিস্টে থাকা আইটেমগুলি মুছুন।

আশাকরি এটা সাহায্য করবে.

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.