RecyclerView: Phát hiện sự không nhất quán. Vị trí mục không hợp lệ


271

QA của chúng tôi đã phát hiện ra một lỗi: khi xoay thiết bị Android (Droid Turbo), đã xảy ra sự cố liên quan đến RecyclerView sau đây :

java.lang.IndexOutOfBoundException: Đã phát hiện sự không nhất quán. Vị trí mục 2 không hợp lệ (offset: 2) .state: 3

Đối với tôi, nó trông giống như một lỗi nội bộ bên trong RecyclerView, vì tôi không thể nghĩ ra bất kỳ cách nào gây ra trực tiếp bởi mã của chúng tôi ...

Có ai gặp phải vấn đề này?

Giải pháp sẽ là gì?

Một cách giải quyết tàn bạo có lẽ có thể bắt ngoại lệ khi nó xảy ra và tạo lại đối tượng RecyclverView từ đầu, để tránh bị bỏ lại với trạng thái bị hỏng.

Nhưng, nếu có thể, tôi muốn hiểu vấn đề tốt hơn (và có lẽ khắc phục nó tại nguồn của nó), thay vì che giấu nó.

Lỗi không dễ sinh sản, nhưng gây tử vong khi xảy ra.

Theo dõi ngăn xếp đầy đủ:

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
    E/AndroidRuntime( 7546): FATAL EXCEPTION: main
    E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
    E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    E/AndroidRuntime( 7546):    at andro

2
Một câu hỏi: Làm thế nào nhất quán là repro của bạn? Tôi biết đây là một lỗi trong mã của google ở đâyđây . Nhưng điều này có thể tránh được. Vì vậy, điều này xảy ra trên mỗi vòng quay?
VicVu

Chào. Nó chỉ xảy ra hiếm khi, nhưng khi nó xảy ra, nó gây tử vong cho ứng dụng.
KarolDepka

Cảm ơn các liên kết đến các lỗi. Cái thứ nhất có vẻ phù hợp hơn cái thứ hai.
KarolDepka

1
Vâng tôi nghĩ rằng đặt cược tốt nhất của bạn là chỉ không cho phép thay đổi đối với listview trong khi xoay vòng.
VicVu

1
Nếu bạn có thể sao chép dễ dàng, tôi sẽ đề nghị in giá trị cho 'getItemCount' trước khi tất cả các cuộc gọi đến 'thông báo *' ... bạn có thể phát hiện ra số lượng vật phẩm của bạn không khớp với giả định của bạn.
Rich Ehmer

Câu trả lời:


209

Tôi đã có một vấn đề liên quan (có thể) - nhập một phiên bản mới của một hoạt động với RecyclerView, nhưng với một bộ chuyển đổi nhỏ hơn đã gây ra sự cố này cho tôi.

RecyclerView.dispatchLayout()có thể cố gắng kéo các mục từ phế liệu trước khi gọi mRecycler.clearOldPositions(). Hậu quả là nó đã kéo các vật phẩm từ nhóm chung có vị trí cao hơn kích thước bộ chuyển đổi.

May mắn thay, nó chỉ thực hiện điều này nếu PredictiveAnimationsđược bật, vì vậy giải pháp của tôi là phân lớp GridLayoutManager( LinearLayoutManagercó cùng vấn đề và 'khắc phục') và ghi đè supportsPredictiveItemAnimations()để trả về false:

/**
 * No Predictive Animations GridLayoutManager
 */
private static class NpaGridLayoutManager extends GridLayoutManager {
    /**
     * Disable predictive animations. There is a bug in RecyclerView which causes views that
     * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
     * adapter size has decreased since the ViewHolder was recycled.
     */
    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public NpaGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }
}

4
Điều này làm việc cho tôi và vô hiệu hóa hoạt ảnh dự đoán không khiến bạn mất tất cả các hoạt hình cùng nhau. Bravo.
Robert Liberatore

8
Cảm ơn ngài rất nhiều! Đã hoạt động ngay lập tức với linearLayoutManager, có thể giúp tôi tiết kiệm nhiều ngày.
levavare

8
Cảm ơn rất nhiều. Giải pháp này được làm việc với linearLayoutManager.
Pruthviraj

8
Tôi nghĩ anh chàng này xứng đáng được chúng tôi xây dựng một bức tượng để vinh danh sự giúp đỡ quý báu của anh ấy ... Đó là một trong những vấn đề tồi tệ nhất trên mạng, nhưng có vẻ như rất nhiều nhà phát triển gặp phải vấn đề này ... Tôi chỉ tự hỏi làm thế nào bạn tìm thấy nó có thể được bỏ qua nếu Dự đoán sai là sai, @KasHunt? Bởi vì, stacktrace rất không rõ ràng ...
PAD

4
Bất cứ ai cũng biết, làm thế nào để khắc phục điều đó mà không cần hack này? Bởi vì thông báoDatasetChanged đã bị loại bỏ vì DiffUtil
Anton Shkurenko

83

Trong trường hợp của tôi (xóa / chèn dữ liệu trong cấu trúc dữ liệu của tôi) Tôi cần xóa nhóm tái chế và sau đó thông báo bộ dữ liệu đã thay đổi!

mRecyclerView.getRecycledViewPool().clear(); mAdapter.notifyDataSetChanged();


6
Tôi thường không nói điều này, NHƯNG CẢM ƠN BẠN RẤT NHIỀU. Tôi đã thử MỌI THỨ để khắc phục sự cố này xảy ra lẻ tẻ khi tôi di chuyển một loạt các mặt hàng trong danh sách xung quanh theo thứ tự nhanh chóng. Tôi đã dành cả tuần có lẽ để cố gắng giải quyết vấn đề này. Tôi đã rời xa nó trong một vài tháng để cố gắng cho bộ não của mình cơ hội tiếp cận nó một cách khác biệt, sau đó tìm thấy điều này trong nỗ lực đầu tiên của Google. Ban phước cho bạn
Chantell Osejo

Hãy để tôi lấy cho bạn một tấn bánh quy vì bạn xứng đáng với từng cái một. Cảm ơn bạn.
antonis_st

tại sao bạn phải làm điều này?
ngày

9
Đó là một hoạt động khá nặng nề mà đánh bại mục đích tái chế quan điểm.
gjsalot

@gjsalot Vậy nếu tôi sử dụng cái này, nó có thể gây ra một số vấn đề không?
Saletanth Karumanaghat 20/03/18

38

Sử dụng notifyDataSetChanged()thay thế notifyItem...trong trường hợp này.


5
Trong một số trường hợp đây là cách để đi. Tôi đã có một tình huống thay thế tất cả các mặt hàng của mình, nhưng tôi không trung thực với bộ điều hợp, chỉ nói với nó rằng tôi đã chèn một số mặt hàng mới (notifyItemRangeInserted), mà không nói trước là tôi cũng đã gỡ bỏ các mặt hàng. Các bộ chuyển đổi sau đó dự kiến ​​sẽ có nhiều mặt hàng hơn sau đó thực sự có. Nếu sử dụng bất kỳ phương thức thông báo nào của bộ điều hợp mong muốn thông báoDataSetChanged, chẳng hạn như notifyItemRangeRemond / Chèn / Cập nhật, người gọi có trách nhiệm hoàn toàn cho biết bộ điều hợp đã thay đổi chính xác , hoặc bạn có thể kết thúc với "trạng thái không nhất quán" này.
JHH

19
Đây không phải là một giải pháp nào cả.
Miha_x64

Đây không phải là con đường để đi. Nếu điều này hoạt động có nghĩa là bạn vừa làm hỏng phạm vi notifyItem...và sửa nó sẽ bắt đầu hoạt động thay vì hiển thị lại tất cả các mục.
Ranjan

12

Tôi đã giải quyết điều này bằng cách trì hoãn mRecycler.setAdapter(itemsAdapter)cho đến khi thêm tất cả các mục vào bộ điều hợp mRecycler.addAll(items)và nó đã hoạt động. Không biết tại sao tôi lại bắt đầu, đó là từ mã của thư viện mà tôi đã xem qua và thấy những dòng đó theo "thứ tự sai", tôi khá chắc chắn rằng đây là mặc dù, xin vui lòng nếu ai đó có thể xác nhận nó giải thích tại sao nó lại giải thích vì thế? Không chắc đây có phải là một câu trả lời hợp lệ hay không


Tôi nghĩ rằng đây là giải pháp, một khi tôi trì hoãn bộ điều hợp thì tôi tin rằng ... bây giờ nó bật lên khi đặt bộ điều hợp trong luồng UI và thêm các mục vào nó.
EngineSense

18
Tôi đã sử dụng swapAdapter(adapter, true)thay vì setAdapter(adapter)và nó đã giúp.
frangulyan

11

Tôi đã có một vấn đề tương tự nhưng không hoàn toàn giống nhau. Trong trường hợp của tôi tại 1 điểm, tôi đã xóa mảng được chuyển đến recyclerview

mObjects.clear();

và không gọi notifyDataSetChanged, vì tôi không muốn recyclerview ngay lập tức xóa các khung nhìn. Tôi đã điền lại mảng mObjects trong AsyncTask.


9

Tôi gặp vấn đề tương tự với recyclerView Vì vậy tôi chỉ thông báo cho bộ điều hợp về thay đổi tập dữ liệu ngay sau khi xóa danh sách.

mList.clear();
mAdapter.notifyDataSetChanged();

mList.addAll(newData);
mAdapter.notifyDataSetChanged();

1
Lỗi đơn giản này khiến tôi mất rất nhiều thời gian, cảm ơn bạn rất nhiều!
leb1755

7

Tôi có một vấn đề tương tự. Nó xảy ra khi tôi đang cuộn nhanh, gọi API và cập nhật dữ liệu. Sau khi thử tất cả mọi thứ để ngăn chặn sự cố, tôi đã tìm thấy giải pháp.

mRecyclerView.stopScroll();

Nó sẽ làm việc.


Đây là một cách giải quyết không phải là một sửa chữa. Bạn đang buộc nó dừng cuộn. Bad UX
Tiến sĩ aNdRO

1
@ Dr.aNdRO: Bộ điều hợp cần đặt vị trí và nếu bạn tiếp tục cuộn recylerview, bộ điều hợp không thể đặt dữ liệu là nguyên nhân gây ra sự cố. Nó không tệ UX
Anand Savć

1
có lý. Dừng cuộn không phải là ux xấu kể từ khi làm mới dữ liệu xảy ra.
Sush

6

Tôi đang thay đổi dữ liệu cho RecyclerViewnền Thread. Tôi đã nhận được giống Exceptionnhư OP. Tôi đã thêm điều này sau khi thay đổi dữ liệu:

myRecyclerView.post(new Runnable() {
    @Override
    public void run() {
        myRecyclerAdapter.notifyDataSetChanged();
    }
});

Hy vọng nó giúp


cảm ơn người đàn ông đây là câu trả lời duy nhất có ý nghĩa theo quan điểm phát triển Android.
dùng347187

Trong khi tôi cũng đã giải quyết với sự giúp đỡ của view.recycler_view.post, tôi đã sử dụng notifyItemInserted. Trong trường hợp của tôi, nó đã là chủ đề UI.
CoolMind

6

Lỗi này xảy ra khi danh sách trong bộ điều hợp bị xóa khi người dùng cuộn khiến vị trí của chủ sở hữu vật phẩm thay đổi, mất tham chiếu giữa danh sách và mục trên ui, xảy ra lỗi trong yêu cầu "notifyDataSetChanged" tiếp theo.

Sửa chữa:

Xem lại phương pháp danh sách cập nhật của bạn. Nếu bạn làm một cái gì đó như

mainList.clear();
...
mainList.add() or mainList.addAll()
...
notifyDataSetChanged();

===> Error occur

Làm thế nào để khắc phục. Tạo đối tượng danh sách mới để xử lý bộ đệm và gán lại cho danh sách chính sau đó

List res = new ArrayList();
…..
res.add();  //add item or modify list
….
mainList = res;
notifyDataSetChanged();

Cảm ơn Nhân Cao vì sự giúp đỡ tuyệt vời này :)


4

Vấn đề của tôi đã biến mất sau khi tôi sửa đổi Adaptertriển khai của mình để sử dụng bản sao của mảng vật phẩm thay vì tham chiếu. Các setItems()phương pháp được gọi là mỗi lần chúng tôi có mặt hàng mới để hiển thị trong RecyclerView.

Thay vì:

private class MyAdapter extends RecyclerView.Adapter<ItemHolder> {
     private List<MyItem> mItems;  

    (....)

    void setItems(List<MyItem> items) {
        mItems = items;
    }
}

Tôi đã làm:

void setItems(List<MyItem> items) {
    mItems = new ArrayList<>(items);
}

Điều này sẽ giải quyết vấn đề, nhưng nó sẽ không chiếm gấp đôi bộ nhớ ban đầu?
Saletanth Karumanaghat

@ MiguelA.Gabriel điều này sẽ ảnh hưởng đến hiệu suất? ví dụ trong trường hợp của tôi, tôi đang cập nhật mảng của recylerview quá thường xuyên nên hiện tại tôi đang làm điều này suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); và đây là nhà xây dựng của tôi public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry

@ mateen-chaudhry Nó có thể sẽ. Bạn sẽ cần kiểm tra nó trong trường hợp của bạn và quyết định, hoặc thử sử dụng một trong những giải pháp được đề xuất. Như tôi đã nói, nó chỉ là một cách giải quyết và nó hoạt động với tôi trong trường hợp của tôi.
Miguel A. Gabriel

3

Tôi đã đối mặt với tình huống tương tự. Và nó đã được giải quyết bằng cách thêm mã trước khi bạn xóa bộ sưu tập của mình.

mRecyclerView.getRecycledViewPool().clear();


3

Trong trường hợp của tôi, tôi đã cập nhật các mục và gọi notifyDataSetChangedtrong một chủ đề không phải là UI. Hầu hết thời gian nó hoạt động, nhưng khi rất nhiều thay đổi xảy ra nhanh chóng, nó sẽ sụp đổ. Khi tôi đã làm, thay vào đó, về cơ bản

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        changeData();
        notifyDataSetChanged();
    }
});

sau đó nó ngừng đâm.


3

Bạn chỉ cần xóa danh sách của bạn OnPostExecute()và không trong khi làmPull to Refresh

// Setup refresh listener which triggers new data loading
        swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {

                AsyncTask<String,Void,String> task = new get_listings();
                task.execute(); // clear listing inside onPostExecute

            }
        });

Tôi phát hiện ra rằng điều này xảy ra khi bạn cuộn trong khi kéo để làm mới , vì tôi đã xóa danh sách trước async task, dẫn đến java.lang.IndexOutOfBoundsException: Inconsistency detected.

        swipeContainer.setRefreshing(false);
        //TODO : This is very crucial , You need to clear before populating new items 
        listings.clear();

Bằng cách đó, bạn sẽ không kết thúc với sự mâu thuẫn


2

Nó cũng có thể liên quan đến việc cài đặt bộ điều hợp nhiều lần cùng một lúc. Tôi đã có một phương thức gọi lại được kích hoạt 5-6 lần cùng một lúc và tôi đang cài đặt bộ điều hợp trong cuộc gọi lại đó để RecyclViewPool không thể xử lý đồng thời với tất cả các dữ liệu đó. Đó là một cơ hội chất béo nhưng tốt hơn bạn nên kiểm tra nó.


1
Có cùng một vấn đề .. Nhưng giải pháp? Bạn chỉ đưa ra lý do .. Làm thế nào để khắc phục?
Ranjith Kumar

@RanjithKumar, Vui lòng chia sẻ cách của bạn để giải quyết vấn đề trên. Tôi đã giải quyết nó bằng mRecyclerView.getRecyclViewPool (). Clear (); trước khi thông báoDataSetChanged và sử dụng khối được đồng bộ hóa xung quanh chức năng cập nhật của bộ chuyển đổi
Attiq ur Rehman

bạn có thể vui lòng xem mã của tôi không, tôi nghĩ vấn đề của tôi giống như của bạn, bạn có thể giúp tôi stackoverflow.com/questions/50213362/iêu
Mateen Chaudhry

2

Sử dụng

notifyDataSetChanged()

thay thế

notifyItemRangeInserted(0, YourArrayList.size())

trong trường hợp này.


1
Nhưng điều này không tốt cho hiệu suất phải không? notifyItemRangeInserted tốt hơn, vấn đề không nằm ở đây
Derekyy

2

Để khắc phục sự cố này, chỉ cần gọi notifyDataSetChanged () với danh sách trống trước khi cập nhật chế độ xem tái chế.

Ví dụ

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // Danh sách để xem tái chế xem

adapter.notifyDataSetChanged();

2
Không phải là một giải pháp.
Miha_x64

@Milha Tôi không nhận được giải pháp nào khác để khắc phục sự cố. Nhưng giải pháp trên là làm việc cho tôi. Nếu nó không phải là một giải pháp, hãy cho tôi biết cách khắc phục thích hợp.
EKN

Nó phụ thuộc. Bạn có thể thử sử dụng DiffUtil - một công cụ đa năng để cập nhật nội dung RecyclerView.
Miha_x64

2

Trong trường hợp của tôi, tôi chỉ cần loại bỏ dòng với setHasStableIds(true);


Nhưng HasStableIds (đúng) cải thiện hiệu suất của Rv, có giải pháp nào khác không?
Sreekanth Karumanaghat

Trên thực tế tôi nghĩ rằng nó có thể là do các lý do khác nhau, vì vậy có thể có các giải pháp khác nhau cho vấn đề này dựa trên nguyên nhân gốc rễ.
Saletanth Karumanaghat

2

Trong trường hợp của tôi, tôi đã cố gắng thay đổi nội dung bộ điều hợp của mình trên một luồng nền nhưng được gọi là notify * trên luồng chính / ui.

Đó là không thể! Lý do tại sao thông báo buộc phải xử lý luồng chính là vì recyclerview muốn bạn chỉnh sửa bộ điều hợp sao lưu của bạn trên luồng chính, ngay cả trên cùng một ngăn xếp cuộc gọi.

Để giải quyết vấn đề, đảm bảo rằng mọi thao tác với bộ điều hợp cũng như mọi cuộc gọi thông báo ... đều được thực hiện trên luồng ui / chính !


2
thêm các mục vào danh sách trong bộ điều hợp của bạn nên được thực hiện trên một chủ đề nền và gọi thông báo trên postexecute. thêm dữ liệu trong luồng ui làm cho ứng dụng đóng băng trong vài mili giây hoặc giây nếu thêm nhiều dữ liệu
dione llorera

đã đồng ý với @dionellorera, cần phải làm rõ rằng "thay đổi nội dung bộ điều hợp" có nghĩa là sửa đổi trực tiếp bất kỳ dữ liệu nào, cho dù giá trị nguyên thủy, thuộc tính của các đối tượng hoặc chính các đối tượng
OzzyTheGiant

2

Tôi đã chạy vào dấu vết ngăn xếp khó chịu này với các Thành phần Kiến trúc Android mới gần đây. Về cơ bản, tôi có một danh sách các mục trong ViewModel của mình được quan sát bởi Fragment của tôi, sử dụng LiveData. Khi ViewModel đăng một giá trị mới cho dữ liệu, Fragment cập nhật bộ điều hợp, chuyển vào các thành phần dữ liệu mới này và thông báo cho bộ điều hợp rằng đã có thay đổi.

Thật không may, khi chuyển các phần tử dữ liệu mới cho bộ điều hợp, tôi đã không tính đến thực tế là cả ViewModel và Bộ điều hợp sẽ được trỏ đến cùng một tham chiếu đối tượng! Có nghĩa là nếu tôi cập nhật dữ liệu và gọipostValue() từ bên trong ViewModel, có một cửa sổ rất nhỏ nơi dữ liệu có thể được cập nhật và bộ điều hợp chưa được thông báo!

Cách khắc phục của tôi là khởi tạo một bản sao mới của các phần tử khi được chuyển vào bộ điều hợp:

mList = new ArrayList<>(passedList);

Với sửa chữa siêu dễ dàng này, bạn có thể được đảm bảo dữ liệu bộ điều hợp của bạn sẽ không thay đổi cho đến khi ngay trước khi bộ điều hợp của bạn được thông báo.


2

Đây chỉ là giải pháp hiệu quả với tôi thậm chí đã thử nhiều giải pháp từ các giải pháp trên.

1.) Thông tin

CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
list.setAdapter(scrollStockAdapter);
scrollStockAdapter.updateList(stockListModels);

2.) Viết phương thức này trong bộ chuyển đổi

public void updateList(List<StockListModel> list) {
stockListModels.clear();
stockListModels.addAll(list);
notifyDataSetChanged();
}

stockListModels -> danh sách này mà bạn đang sử dụng trong bộ điều hợp.


2

Đối với tôi, nó hoạt động sau khi thêm dòng mã này:

mRecyclerView.setItemAnimator(null);

2
Đây không phải là một sửa chữa trong hầu hết các trường hợp, nếu bạn muốn hoạt hình, bạn phải viết lại mã bộ điều hợp và tìm lỗi của mình để thông báo các thay đổi
Dragos Rachieru 21/03/19

Nó hoạt động tốt với tôi. Tôi đang sử dụng bộ điều hợp thực vì vậy tôi không thể kiểm soát luồng và tôi đã bật windowActivityTransitions theo kiểu gây ra sự cố này nhờ người đàn ông bạn cứu tôi.
Arul Mani

1

vấn đề này có thể xảy ra khi bạn thử xóa danh sách của mình, nếu bạn định xóa danh sách dữ liệu của mình đặc biệt là khi bạn đang sử dụng pull để làm mới hãy thử sử dụng cờ boolean, khởi tạo nó là false và bên trong phương thức OnRefresh làm cho nó đúng, xóa dữ liệu của bạn nếu cờ là đúng ngay trước khi thêm dữ liệu mới vào đó và sau đó làm cho nó sai.

mã của bạn có thể như thế này

 private boolean pullToRefreshFlag = false ;
 private ArrayList<your object> dataList ;
 private Adapter adapter ;

 public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{

 private void requestUpdateList() {

     if (pullToRefresh) {
        dataList.clear
        pullToRefreshFlag = false;
     }

     dataList.addAll(your data);
     adapter.notifyDataSetChanged;


 @Override
 OnRefresh() {
 PullToRefreshFlag = true
 reqUpdateList() ; 
 }

}

1

Tôi đã có một vấn đề tương tự trước đây. Cuối cùng tìm thấy một cách giải quyết cho điều đó

Những gì tôi làm là thông báo cho bộ điều hợp rằng mục đã bị xóa và sau đó thông báo phạm vi bộ dữ liệu của bộ điều hợp đã thay đổi

 public void setData(List<Data> dataList) {
      if (this.dataList.size() > 0) {
          notifyItemRangeRemoved(0, dataList.size());
          this.dataList.clear();
      }
      this.dataList.addAll(dataList)
      notifyItemRangeChanged(0, dataList.size());

 }

1

Tôi gặp phải một vấn đề tương tự và chỉ cần tìm ra nó. Tôi đã mã hóa một vài ví dụ cho một trường hợp thử nghiệm nhưng không đảm bảo rằng mỗi cái đều trả về một ID duy nhất và điều đó gây ra sự cố bên dưới cho tôi. Sửa ID đã giải quyết vấn đề, hy vọng điều này sẽ giúp người khác!


1

tôi cũng từng có lỗi

Nguyên nhân: Tôi đã cố cập nhật Chế độ xem tái chế từ tác vụ Async trong khi đồng thời cố gắng để có được các khung nhìn đã bị xóa cũ;

Mã: Tôi tạo dữ liệu chỉ bằng cách nhấn nút, logic như sau

  1. Xóa các mục cuối cùng trong chế độ xem tái chế
  2. Gọi tác vụ async để tạo dữ liệu
  3. OnPostExecute Cập nhật khung nhìn Recycler và NotifyDataSetChanged

Vấn đề: Bất cứ khi nào tôi cuộn nhanh trước khi tạo dữ liệu của mình, tôi nhận được

Phát hiện sự không nhất quán. Vị trí bộ điều hợp chủ sở hữu chế độ xem không hợp lệ Xem java.lang.IndexOutOfBoundException: Phát hiện sự không nhất quán. Vị trí mục 20 không hợp lệ (offset: 2) .state: 3

Giải pháp: thay vì xóa RecyclerView trước khi tạo dữ liệu của tôi, thay vào đó tôi để lại nó và sau đó Thay thế nó bằng Dữ liệu mới, Cuộc gọi NotifyDatasetChanged, như hiển thị bên dưới;

       @Override
        protected void onPostExecute(List<Objects> o) {
            super.onPostExecute(o);
            recyclerViewAdapter.setList(o);
            mProgressBar.setVisibility(View.GONE);
            mRecyclerView.setVisibility(View.VISIBLE);
        }

bạn có thể xin vui lòng xem xét mã của tôi tôi nghĩ rằng vấn đề của tôi là như của bạn [link] ( stackoverflow.com/questions/50213362/... )
Mateen Chaudhry

1

Chỉ cần xóa tất cả các chế độ xem của Trình quản lý bố cục của bạn trước khi thông báo. giống:

myLayoutmanager.removeAllViews();

Nó hoạt động. Tôi gặp vấn đề với tải cuộn và thay đổi tab.
Warwicky

1

Sử dụng ListAdapter (androidx.recyclerview.widget.ListAdapter)cuộc gọi adapter.submitList(null)trước khi gọi adapter.submitList(list):

adapter.submitList(null)
adapter.submitList(someDataList)

1

Ngoại lệ này được nêu ra trên API 19, 21 (nhưng không mới). Trong Kotlin coroutine, tôi đã tải dữ liệu (trong luồng nền) và trong luồng UI được thêm vào và hiển thị chúng:

adapter.addItem(item)

Bộ chuyển đổi:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

open fun addItem(item: Item) {
    list.add(item)
    notifyItemInserted(list.lastIndex)
}

Vì một số lý do, Android không hiển thị đủ nhanh hoặc thứ gì khác, vì vậy, tôi cập nhật danh sách theo postphương pháp RecyclerView(thêm, xóa, cập nhật các sự kiện của các mục):

view.recycler_view.post { adapter.addItem(item) }

Ngoại lệ này tương tự như "Không thể gọi phương thức này trong cuộc gọi lại cuộn. Cuộc gọi lại cuộn có thể chạy trong một lần đo và bố trí mà bạn không thể thay đổi dữ liệuRecyclerView. khung tiếp theo. ": Recyclerview - không thể gọi phương thức này trong một cuộc gọi lại cuộn .


0

Tôi thấy rằng cài đặt mRecycler.setLayoutF Frozen (true); trong phương thức onRefresh của swipeContainer.

giải quyết vấn đề cho tôi

swipeContainer.setOnRefreshListener(new   SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            orderlistRecycler.setLayoutFrozen(true);
            loadData(false);

        }
    });

0

Đây là một lỗi khá khó chịu.

Để xử lý nhấp chuột mục của tôi, tôi đã sử dụng một triển khai RecyclerView.OnItemTouchListenertương tự như giải pháp được tìm thấy trong câu hỏi này .

Sau nhiều lần làm mới RecyclerViewnguồn dữ liệu và nhấp vào một mục, điều này IndexOutOfBoundsExceptionsẽ làm hỏng ứng dụng của tôi. Khi một mục được nhấp vào, RecyclerViewbên trong sẽ tìm kiếm chế độ xem bên dưới chính xác và trả lại vị trí của nó. Kiểm tra mã nguồn, tôi thấy rằng có một số TasksThreads lên lịch. Để cắt ngắn câu chuyện, về cơ bản, đó chỉ là một trạng thái bất hợp pháp trong đó hai nguồn dữ liệu được trộn lẫn và không được đồng bộ hóa và toàn bộ sự việc trở nên điên rồ.

Dựa trên điều này, tôi loại bỏ thực hiện của tôi về RecyclerView.OnItemTouchListenervà chỉ cần bắt được nhấp chuột trên ViewHoldercủa Adapterbản thân mình:

public void onBindViewHolder (final BaseContentView holder, final int position) {

    holder.itemView.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick (View view) {

        // do whatever you like here
      }
    });

}

Đây có thể không phải là giải pháp tốt nhất, nhưng hiện tại không có sự cố .. Hy vọng rằng điều này sẽ giúp bạn tiết kiệm thời gian :).


Tạo một đối tượng mới mỗi lần onBind được gọi sẽ dẫn đến rất nhiều đối tượng sẽ bị thu gom rác và người dùng có thể gặp phải tình trạng đóng băng.
dephinera
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.