Android, ListView IllegalStateException: Đá Nội dung của bộ chuyển đổi đã thay đổi nhưng ListView không nhận được thông báo


188

Những gì tôi muốn làm : chạy một luồng nền để tính toán nội dung ListView và cập nhật ListView một phần, trong khi kết quả được tính toán.

Những gì tôi biết tôi phải tránh : Tôi không thể gây rối với nội dung ListAd CHƯƠNG từ luồng nền, vì vậy tôi đã kế thừa AsyncTask và xuất bản kết quả (thêm mục vào bộ điều hợp) từ onProTHERUpdate. Bộ điều hợp của tôi sử dụng ArrayList của các đối tượng kết quả, tất cả các hoạt động trên các danh sách mảng đó được đồng bộ hóa.

Nghiên cứu của người khác : có dữ liệu rất có giá trị ở đây . Tôi cũng bị sự cố gần như hàng ngày đối với nhóm ~ 500 người dùng và khi tôi thêm list.setVisibility(GONE)/trackList.setVisibility(VISIBLE)khối vào onProTHERUpdate, các sự cố bị hạ thấp bởi hệ số 10 nhưng không biến mất. (nó đã được đề xuất trong câu trả lời )

Những gì tôi nhận được đôi khi : xin lưu ý, nó thực sự hiếm khi xảy ra (một lần một tuần cho một trong số 3,5k người dùng). Nhưng tôi muốn loại bỏ hoàn toàn lỗi này. Đây là stacktrace một phần:

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

Cứu giúp? Không cần thiết nữa, xem bên dưới

TRẢ LỜI CUỐI CÙNG: Khi nó bật ra, tôi đã gọi notifyDataSetChangedmỗi 5 lần chèn để tránh nhấp nháy và thay đổi danh sách đột ngột. Nó không thể được thực hiện theo cách đó, luôn thông báo cho bộ điều hợp khi danh sách cơ sở thay đổi. Lỗi này nó đã mất từ ​​lâu đối với tôi bây giờ.


3
Bạn đã gọi notifyDataSetChanged () chưa?
Denis Palnitsky

1
Tất nhiên, trong onProgressUpdate có đi trình tự: list.setVisibility (đi) - addObjectToList / đồng bộ hoạt động trên danh sách / - notifyDataSetChanged () - list.setVisibility (visible) (và không có thay đổi tầm nhìn ngoại lệ xảy ra thường xuyên hơn)
tomash

1
Bạn đang sửa đổi ArrayList cơ bản trên một luồng khác? Tất cả các thay đổi, ngay cả đối với ArrayList được Bộ điều hợp tham chiếu, phải xảy ra trên luồng UI.
Người lập kế hoạch giàu có

2
@Qberticus - như tôi đã nói rõ ràng, tôi không sửa đổi ArrayList từ các luồng khác nhau, nhưng từ phương thức onProTHERUpdate của AcyncTask - nó hoạt động trong luồng GUI.
tomash

1
@tomash tôi có ngoại lệ tương tự tôi đang sử dụng pull để làm mới và trong bài thực thi tôi đã viết bộ chuyển đổi.notifyDataSetChanged (); lvAutherlist.completeRefreshing (); nhưng đôi khi có lỗi này làm thế nào để giải quyết nó
Khan

Câu trả lời:


119

Tôi gặp vấn đề tương tự.

Tôi đã thêm các mục vào ArrayListbên ngoài chuỗi UI.

Giải pháp: Tôi đã thực hiện cả hai adding the itemsvà được gọi notifyDataSetChanged()trong luồng UI.


4
Tôi đã thêm các mục và gọi notifyDataSetChanged () trong luồng UI và tôi đã giải quyết điều này.
Mullins

23
Nhận xét về thể loại, thực sự.
vết lõm

2
Đây là một vấn đề đa luồng và sử dụng các khối được đồng bộ hóa đúng cách Điều này có thể được ngăn chặn. Không đặt thêm những thứ khác vào UI Thread và gây mất khả năng phản hồi của ứng dụng. Hãy xem câu trả lời của tôi dưới đây.
Javanator

2
Đối với những người đọc trong tương lai - thực hiện các phép tính trong luồng nền là một điều tốt, nhưng bạn nên chuyển kết quả cho luồng UI và thực hiện thêm các mục vào bộ sưu tập cơ sở bộ điều hợp và thông báo bộ điều hợp trong cùng một khối mã.
tomash

4
@Mullins Bạn có thể vui lòng hiển thị một mẫu cho giải pháp của mình không? Tôi bị kẹt ở cùng một vấn đề
Jas

27

Tôi đã có cùng một vấn đề, nhưng tôi đã sửa nó bằng phương pháp

requestLayout();

từ lớp ListView


35
Khi nào chúng ta nên gọi phương pháp này?
JehandadK

3
@DeBuGGeR sau khi bạn thêm các mục vào ListView hoặc thay đổi bộ điều hợp của nó.
Ahmet Noyan Kızıltan

Điều gì xảy ra nếu tôi thêm các yếu tố trong asynctask
Tiến sĩ aNdRO

2
Chỉ để ghi lại, @ Dr.aNdRO, bạn thu thập kết quả của mình trong AsyncTask. doInBackground () và lưu kết quả của bạn để cập nhật danh sách trong AsyncTask.onPostExecute (), sẽ chạy trong luồng UI. Nếu bạn cần cập nhật khi bạn đi, hãy sử dụng AsyncTask.publishProTHER () và AsyncTask.onProTHERUpdate () cũng chạy trong luồng UI.
Nicole Borrelli

21

Đây là một vấn đề MultiThreading và sử dụng các khối được đồng bộ hóa đúng cách Điều này có thể được ngăn chặn. Không đặt thêm những thứ khác vào UI Thread và gây mất khả năng phản hồi của ứng dụng.

Tôi cũng phải đối mặt như vậy. Và vì câu trả lời được chấp nhận nhiều nhất cho thấy việc thay đổi dữ liệu bộ điều hợp từ UI Thread có thể giải quyết vấn đề. Điều đó sẽ làm việc nhưng là một giải pháp nhanh chóng và dễ dàng nhưng không phải là giải pháp tốt nhất.

Như bạn có thể thấy cho một trường hợp bình thường. Cập nhật bộ điều hợp dữ liệu từ luồng nền và gọi notifyDataSetChanged trong giao diện người dùng hoạt động.

Bất hợp phápStateException này phát sinh khi một luồng ui đang cập nhật khung nhìn và một luồng nền khác thay đổi dữ liệu một lần nữa. Khoảnh khắc đó gây ra vấn đề này.

Vì vậy, nếu bạn sẽ đồng bộ hóa tất cả các mã đang thay đổi dữ liệu bộ điều hợp và thực hiện cuộc gọi notifydatasetchange. Vấn đề này nên được biến mất. Như đã đi cho tôi và tôi vẫn đang cập nhật dữ liệu từ chủ đề nền.

Đây là mã trường hợp cụ thể của tôi để người khác tham khảo.

Trình tải của tôi trên màn hình chính sẽ tải các số liên lạc trong danh bạ vào nguồn dữ liệu của tôi trong nền.

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

PhoneBookManager.getPhoneBookLink này đọc số liên lạc từ danh bạ và điền chúng vào hashmap. Có thể sử dụng trực tiếp để Bộ điều hợp Danh sách để vẽ danh sách.

Có một nút trên màn hình của tôi. Điều đó mở ra một hoạt động trong đó những số điện thoại được liệt kê. Nếu tôi trực tiếp thiết lập Chương trình qua danh sách trước khi luồng trước hoàn thành công việc thì đó là trường hợp điều hướng nhanh xảy ra ít thường xuyên hơn. Nó bật lên ngoại lệ. Ai là tiêu đề của câu hỏi SO này. Vì vậy, tôi phải làm một cái gì đó như thế này trong hoạt động thứ hai.

Trình tải của tôi trong hoạt động thứ hai chờ đợi luồng đầu tiên hoàn thành. Cho đến khi nó hiển thị một thanh tiến trình. Kiểm tra loadInBackground của cả hai bộ tải.

Sau đó, nó tạo ra bộ điều hợp và phân phối nó đến hoạt động trong đó trên chủ đề ui tôi gọi setAd CHƯƠNG.

Điều đó đã giải quyết vấn đề của tôi.

Mã này chỉ là một đoạn. Bạn cần thay đổi nó để biên dịch tốt cho bạn.

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

}

Hi vọng điêu nay co ich


Xin lỗi, bạn đã làm điều đó như thế nào? Làm thế nào bạn đã đồng bộ hóa mã chủ đề nền và cuộc gọi notifydatasetchange? Tôi đang sử dụng doInBackground khi lọc dữ liệu trên AutocompleteTextView và tôi đang gặp vấn đề tương tự ... Bạn có thể tư vấn cho tôi điều gì để giải quyết không?
tonix

@ user3019105 - Đồng bộ hóa hai luồng nền. một trong đó cập nhật dữ liệu ở chế độ nền và khác để thông báo luồng ui cho setAdadpter hoặc notifydatasetchanged bằng các phương thức handler.post hoặc các phương thức runOnUiThread có sẵn. Đối với trường hợp cụ thể của tôi, tôi đã đồng bộ hóa hai trình tải với doInBackground được đồng bộ hóa trên một đối tượng singleton. Một bắt đầu chuẩn bị dữ liệu trên màn hình giật gân để nhanh chóng có sẵn cho màn hình bảng điều khiển. Đó là nhu cầu của tôi.
Javanator

Bạn có thể vui lòng gửi một đoạn mã của việc thực hiện này không? Trong trường hợp của tôi, tôi đã giải quyết việc gọi Clear () trên bộ điều hợp, tạo một ArrayList tạm thời <T> của các đối tượng được lọc, sau đó thực hiện cuộc gọi đến addAll (tmpArrayList) và cuối cùng là notifyDataSetChanged (). Tôi thực hiện tất cả các cuộc gọi này bên trong phương thức FilterResult (), chạy trên luồng chính. Bạn có nghĩ rằng đây là một giải pháp đáng tin cậy?
tonix

@ user3019105 kiểm tra câu trả lời đã chỉnh sửa. Hy vọng nó hữu ích cho bạn bây giờ
Javanator

Câu trả lời này đòi hỏi một rất nhiều đọc về lý do tại sao nó hoạt động, tutorials.jenkov.com/java-concurrency/synchronized.html là một nơi tốt để bắt đầu
Abdu

15

Tôi đã giải quyết điều này bằng cách có 2 Danh sách. Một danh sách tôi chỉ sử dụng cho bộ điều hợp và tôi thực hiện tất cả các thay đổi / cập nhật dữ liệu trên danh sách khác. Điều này cho phép tôi thực hiện cập nhật trên một danh sách trong luồng nền, sau đó cập nhật danh sách "bộ điều hợp" trong luồng chính / 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

Tôi đã viết mã này và nó chạy trong hình ảnh giả lập 2.1 trong 12 giờ và không nhận được IllegalStateException. Tôi sẽ cung cấp cho khung công tác Android lợi ích của sự nghi ngờ về điều này và nói rằng rất có thể đó là lỗi trong mã của bạn. Tôi hi vọng cái này giúp được. Có lẽ bạn có thể điều chỉnh nó vào danh sách và dữ liệu của bạn.

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

Cám ơn về công việc của bạn! Tôi nhận thấy rằng đối với phương thức ArrayAd CHƯƠNG hasStableIds () = false; việc triển khai của tôi đã trả về đúng, điều này không hoàn toàn đúng, vì các hàng được sắp xếp trước mỗi notifyDataSetChanged (). Tôi sẽ thử nó - nếu sự cố còn tồn tại, tôi sẽ tiếp tục điều tra. Trân trọng!
tomash

4

Vài ngày trước tôi đã gặp vấn đề tương tự và gây ra hàng ngàn vụ tai nạn mỗi ngày, khoảng 0,1% người dùng gặp tình huống này. Tôi đã thử setVisibility(GONE/VISIBLE)requestLayout() , nhưng số lượng sự cố chỉ giảm một chút.

Và cuối cùng tôi đã giải quyết nó. Không có gì với setVisibility(GONE/VISIBLE). Không có gì với requestLayout().

Cuối cùng tôi tìm thấy lý do là tôi đã sử dụng Handlerđể gọi notifyDataSetChanged()sau khi cập nhật dữ liệu, điều này có thể dẫn đến một loại:

  1. Cập nhật dữ liệu cho một đối tượng mô hình (tôi gọi nó là DataSource)
  2. Người dùng chạm vào listview (có thể gọi checkForTap()/ onTouchEvent()và cuối cùng là cuộc gọi layoutChildren())
  3. Bộ điều hợp lấy dữ liệu từ đối tượng mô hình và gọi notifyDataSetChanged()và cập nhật chế độ xem

Và tôi đã mắc một lỗi khác là getCount(), getItem()getView(), tôi trực tiếp sử dụng các trường trong DataSource, thay vì sao chép chúng vào bộ điều hợp. Vì vậy, cuối cùng nó sụp đổ khi:

  1. Bộ điều hợp cập nhật dữ liệu mà phản hồi cuối cùng mang lại
  2. Khi phản hồi tiếp theo trở lại, DataSource cập nhật dữ liệu, điều này khiến số lượng vật phẩm thay đổi
  3. Người dùng chạm vào listview, có thể là một cú chạm hoặc di chuyển hoặc lật
  4. getCount()getView()được gọi và listview thấy dữ liệu không nhất quán và đưa ra các ngoại lệ như thế nào java.lang.IllegalStateException: The content of the adapter has changed but.... Một ngoại lệ phổ biến khác là IndexOutOfBoundExceptionnếu bạn sử dụng tiêu đề / chân trang trong ListView.

Vì vậy, giải pháp rất dễ dàng, tôi chỉ cần sao chép dữ liệu vào bộ điều hợp từ DataSource của mình khi Trình xử lý kích hoạt bộ điều hợp để nhận dữ liệu và cuộc gọi notifyDataSetChanged(). Sự cố bây giờ không bao giờ xảy ra nữa.


1
Đây là vấn đề của tôi. Trong trường hợp của tôi, getCount () đã trả về số mới có sẵn trước khi notifyDataSetChanged được gọi. Vì danh sách của tôi là 'ảo', bây giờ tôi nắm bắt số đếm được cập nhật trong phương thức notifyDataSetChanged của mình và sau đó trả về số lượng được lưu trong bộ nhớ cache này khi được yêu cầu đếm qua getCount ().
Glenn

3

Nếu điều này xảy ra không liên tục, hóa ra tôi chỉ gặp vấn đề này khi danh sách được cuộn sau khi nhấp vào mục cuối cùng 'tải thêm'. Nếu danh sách không được cuộn, mọi thứ đều hoạt động tốt.

Sau khi gỡ lỗi RẤT NHIỀU, đó là một lỗi của tôi, nhưng cũng không nhất quán trong mã Android.

Khi xác thực xảy ra, mã này được thực thi trong 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 "

Nhưng khi onChange xảy ra, nó kích hoạt mã này trong AdapterView (cha mẹ của ListView)

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

Lưu ý cách Bộ điều hợp KHÔNG được đảm bảo giống nhau!

Trong trường hợp của tôi, vì đó là 'LoadMoreAd CHƯƠNG', tôi đã trả lại WrappingAd CHƯƠNG trong lệnh gọi getAd CHƯƠNG (để truy cập vào các đối tượng cơ bản). Điều này dẫn đến số lượng khác nhau do có thêm mục 'Tải thêm' và Ngoại lệ bị ném.

Tôi chỉ làm điều này bởi vì các tài liệu làm cho nó có vẻ như ổn để làm

ListView.getAd CHƯƠNG javadoc

Trả về bộ điều hợp hiện đang được sử dụng trong ListView này. Bộ điều hợp được trả về có thể không phải là bộ điều hợp tương tự được chuyển cho setAd CHƯƠNG (ListAd CHƯƠNG) nhưng có thể là WrapperListAd CHƯƠNG.


Tôi gặp một vấn đề tương tự . Bạn có thể vui lòng đề nghị làm thế nào để sửa chữa nó.
May13ank 14/05/2015

Nếu bạn nhìn vào 2 mẫu mã ở trên, bạn sẽ thấy mAd CHƯƠNG trong ListView và getAd CHƯƠNG () trong phần gốc của ListView (AdapterView). Nếu bạn ghi đè getAd CHƯƠNG (), kết quả của getAd CHƯƠNG () có thể không phải là mAd CHƯƠNG. Nếu số lượng của 2 Bộ điều hợp khác nhau, bạn sẽ gặp lỗi.
aaronvargas

3

Vấn đề của tôi liên quan đến việc sử dụng Bộ lọc cùng với ListView.

Khi cài đặt hoặc cập nhật mô hình dữ liệu cơ bản của ListView, tôi đã làm một cái gì đó như thế này:

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

Gọi filter()ở dòng cuối cùng sẽ (và phải) khiến notifyDataSetChanged()được gọi trong Bộ lọcpublishResults() phương thức . Điều này đôi khi có thể hoạt động tốt, đặc biệt là trong Nexus 5. nhanh của tôi, nhưng thực tế, nó ẩn một lỗi mà bạn sẽ nhận thấy với các thiết bị chậm hơn hoặc trong điều kiện sử dụng nhiều tài nguyên.

Vấn đề là quá trình lọc được thực hiện không đồng bộ và do đó, giữa phần cuối của filter()câu lệnh và lệnh gọi publishResults(), cả trong luồng UI, một số mã luồng UI khác có thể thực thi và thay đổi nội dung của bộ điều hợp.

Khắc phục thực tế rất dễ dàng, chỉ cần gọi notifyDataSetChanged()trước khi yêu cầu lọc được thực hiện:

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

3

Tôi có một Danh sách nếu các đối tượng Feed. Nó được nối và cắt từ luồng không có UI. Nó hoạt động tốt với bộ chuyển đổi dưới đây. Tôi gọi FeedAdapter.notifyDataSetChangedtrong UI UI dù sao nhưng một lát sau. Tôi thích điều này vì các đối tượng Feed của tôi vẫn còn trong bộ nhớ trong Dịch vụ cục bộ ngay cả khi UI đã chết.

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

Tôi đã phải đối mặt với cùng một vấn đề với chính xác cùng một bản ghi lỗi. Trong trường hợp của tôi onProgress(), AsyncTask sẽ thêm các giá trị vào bộ điều hợp bằng cách sử dụng mAdapter.add(newEntry). Để tránh giao diện người dùng trở nên ít phản hồi hơn, tôi đặt mAdapter.setNotifyOnChange(false)và gọi mAdapter.notifyDataSetChanged()4 lần lần thứ hai. Mỗi giây một lần, mảng được sắp xếp.

Điều này hoạt động tốt và trông rất gây nghiện, nhưng thật không may là có thể đánh sập nó bằng cách chạm vào các mục danh sách hiển thị thường xuyên đủ.

Nhưng có vẻ như tôi đã tìm thấy một cách giải quyết chấp nhận được. Tôi đoán rằng ngay cả khi bạn chỉ làm việc trên luồng ui, bộ điều hợp không chấp nhận nhiều thay đổi đối với dữ liệu của nó mà không gọi notifyDataSetChanged(), vì điều này tôi đã tạo một hàng đợi lưu trữ tất cả các mục mới cho đến khi hết 300ms đã đề cập. Nếu đạt được khoảnh khắc này, tôi thêm tất cả các mục được lưu trữ trong một lần chụp và gọi notifyDataSetChanged(). Cho đến bây giờ tôi đã không thể phá vỡ danh sách nữa .



2

Ngay cả tôi cũng gặp phải vấn đề tương tự trong ứng dụng thông báo XMPP của mình, thông báo người nhận cần được thêm lại vào chế độ xem danh sách (được triển khai với ArrayList). Khi tôi cố gắng thêm nội dung máy thu thông qua MessageListener(luồng riêng biệt), ứng dụng thoát với lỗi trên. Tôi đã giải quyết điều này bằng cách thêm nội dung vào phương thức arraylist& setListviewadapaterthông qua của tôi runOnUiThread, một phần của lớp Activity. Điều này đã giải quyết vấn đề của tôi.


1

Tôi đã đối mặt với một vấn đề tương tự, đây là cách tôi giải quyết trong trường hợp của mình. Tôi xác minh nếu taskđã là RUNNINGhoặc FINISHEDbởi vì một tác vụ chỉ có thể chạy một lần. Dưới đây bạn sẽ thấy một phần mã và điều chỉnh từ giải pháp của tôi.

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

Tôi đã có cùng một vấn đề và tôi đã giải quyết nó. Vấn đề của tôi là tôi đã sử dụng một listview, với một bộ chuyển đổi mảng và với bộ lọc. Trên phương thức performFilteringtôi đã làm rối với mảng có dữ liệu và đó là vấn đề vì phương thức này không chạy trên luồng UI và EVENTUALLY nó gây ra một số vấn đề.


1

Một nguyên nhân cho sự cố này là ArrayListđối tượng không thể thay đổi hoàn toàn. Vì vậy, khi tôi xóa một mục, tôi phải làm điều này:

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

Điều này đã khắc phục sự cố cho tôi.


1

Trong trường hợp của tôi, tôi đã gọi phương thức GetFilter()trên một bộ chuyển đổi từ TextWatcher()phương thức trên Hoạt động chính và tôi đã thêm dữ liệu với vòng lặp For trên GetFilter(). Giải pháp là thay đổi vòng lặp For thành AfterTextChanged()phương thức phụ trên Hoạt động chính và xóa cuộc gọi đếnGetFilter()


1
Hãy làm rõ giải pháp của bạn. Tôi cũng đang ở trong tình huống giống như bạn /
nAkhmedov

1
adapter.notifyDataSetChanged()

2
Đó là cách thực hành tốt trên Stack Overflow để thêm một lời giải thích về lý do tại sao giải pháp của bạn nên hoạt động hoặc tốt hơn các giải pháp hiện có. Để biết thêm thông tin đọc Làm thế nào để trả lời .
Samuel Liew

0

Tôi cũng đã nhận được chính xác lỗi tương tự và sử dụng 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

Tôi đã giải quyết nó bằng cách đặt adapter.notifyDataSetChanged();ở dưới cùng của luồng UI, đó là phương thức AsyncTask onPostExecute của tôi. Như thế này :

 protected void onPostExecute(Void aVoid) {

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

           adapter.notifyDataSetChanged();

                }

            });
        }

Bây giờ ứng dụng của tôi hoạt động.

EDIT: Trên thực tế, ứng dụng của tôi vẫn bị lỗi khoảng 1 trong 10 lần, gây ra lỗi tương tự.

Cuối cùng, tôi đã gặp runOnUiThreadmột bài viết trước đây, mà tôi nghĩ rằng nó có thể hữu ích. Vì vậy, tôi đặt nó trong phương thức doInBackground của mình, như thế này:

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

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

Và tôi đã loại bỏ adapter.notifyDataSetChanged();phương pháp. Bây giờ, ứng dụng của tôi không bao giờ gặp sự cố.


0

Vui lòng thử một trong những giải pháp sau:

  1. Đôi khi, nếu bạn thêm đối tượng mới vào danh sách dữ liệu trong một luồng (hoặc doInBackgroundphương thức), lỗi này sẽ xảy ra. Giải pháp là: tạo một danh sách tạm thời và thêm dữ liệu vào danh sách này trong luồng (hoặc doInBackground), sau đó sao chép tất cả dữ liệu từ danh sách tạm thời vào danh sách bộ điều hợp trong luồng UI (hoặc onPostExcute)

  2. Đảm bảo rằng tất cả các cập nhật UI được gọi trong luồng UI.


0

tôi đã gặp vấn đề tương tự khi thêm dữ liệu mới vào trình tải hình ảnh lười biếng tôi chỉ cần đặt

         adapter.notifyDataSetChanged();

trong

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

hy vọng nó sẽ giúp bạn


0

Giống như @Mullins đã nói "
Tôi đã thêm các mục và gọi notifyDataSetChanged()trong chuỗi UI và tôi đã giải quyết vấn đề này. - Mullins".

Trong trường hợp của tôi, tôi có asynctaskvà tôi gọi notifyDataSetChanged()trong doInBackground()phương pháp và vấn đề được giải quyết, khi tôi gọi từ onPostExecute()tôi nhận được ngoại lệ.


0

Tôi đã có một tùy chỉnh ListAdaptervà đã gọi super.notifyDataSetChanged()từ đầu chứ không phải kết thúc phương thức

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

0

Tôi đã có cùng một số tiền, tôi đã có nhiều nhóm mông trong danh sách của mình trên listview và tôi đã thay đổi một số giá trị boolean bên trong mục của tôi như chủ sở hữu.rbVar.setOnclik ...

vấn đề của tôi xảy ra bởi vì tôi đang gọi một phương thức bên trong getView (); và đang lưu một đối tượng bên trong sharepreference, vì vậy tôi có lỗi tương tự ở trên

Làm thế nào tôi giải quyết nó; Tôi đã xóa phương thức của mình bên trong getView () để thông báo choDataInInvalidated () và vấn đề đã biến mất

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

0

Tôi đã từng gặp vấn đề tương tự. cuối cùng tôi đã có giải pháp

trước khi cập nhật listview, nếu bàn phím mềm xuất hiện, hãy đóng nó trước. sau đó thiết lập nguồn dữ liệu và gọi notifydatasetchanged ().

Trong khi đóng bàn phím nội bộ listview sẽ cập nhật ui của nó. nó tiếp tục gọi cho đến khi đóng bàn phím. thời điểm đó nếu nguồn dữ liệu thay đổi, nó sẽ ném ngoại lệ này. nếu dữ liệu đang cập nhật trong onActivityResult, có thể xảy ra lỗi tương tự.

 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

Giải pháp của tôi:

1) tạo a temp ArrayList.

2) thực hiện các công việc nặng của bạn (tìm nạp hàng sqlite, ...) trong doInBackgroundphương thức và thêm các mục vào danh sách mảng tạm thời.

3) thêm tất cả các mục từ danh sách tạm thời vào danh sách mảng của listview trong onPostExecutephương thức.

note:bạn có thể muốn xóa một số mục khỏi listview và cũng có thể xóa khỏi cơ sở dữ liệu sqlite và có thể xóa một số tệp liên quan đến các mục từ sdcard, chỉ cần xóa các mục khỏi cơ sở dữ liệu và xóa các tệp liên quan của chúng và thêm chúng vào danh sách mảng tạm thời background thread. sau đó trongUI thread xóa các mục hiện có trong danh sách mảng tạm thời khỏi danh sách mảng của listview.

Hi vọng điêu nay co ich.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.