RecyclerView: ตรวจพบความไม่สอดคล้องกัน ตำแหน่งรายการไม่ถูกต้อง


271

QA ของเราตรวจพบข้อผิดพลาด: เมื่อหมุนอุปกรณ์ Android (Droid Turbo) เกิดข้อผิดพลาดที่เกี่ยวข้องกับ RecyclerViewต่อไปนี้:

java.lang.IndexOutOfBoundsException: ตรวจพบความไม่สอดคล้องกัน ตำแหน่งรายการไม่ถูกต้อง 2 (ชดเชย: 2). สถานะ: 3

สำหรับฉันดูเหมือนว่าข้อผิดพลาดภายในใน RecyclerView เนื่องจากฉันไม่สามารถนึกถึงวิธีการใด ๆ ที่เกิดจากรหัสของเราโดยตรง ...

มีใครพบปัญหานี้หรือไม่?

อะไรจะแก้ปัญหาได้?

วิธีแก้ปัญหาที่โหดร้ายอาจจะสามารถจับข้อยกเว้นเมื่อมันเกิดขึ้นและสร้างอินสแตนซ์ของ RecyclverView อีกครั้งตั้งแต่ต้นเพื่อหลีกเลี่ยงการถูกทิ้งให้อยู่ในสถานะที่เสียหาย

แต่ถ้าเป็นไปได้ฉันต้องการทำความเข้าใจปัญหาให้ดีขึ้น (และอาจแก้ไขได้ที่แหล่งที่มา) แทนการปิดบัง

ข้อผิดพลาดไม่ใช่เรื่องง่ายที่จะทำซ้ำ แต่มันเป็นอันตรายถึงชีวิตเมื่อมันเกิดขึ้น

การติดตามสแต็กเต็ม:

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
คำถาม: ความสอดคล้องของคุณมีความสอดคล้องกันอย่างไร? ฉันรู้ว่านี้เป็นข้อผิดพลาดในรหัสของ Google ที่นี่และที่นี่ แต่สิ่งนี้สามารถหลีกเลี่ยงได้ ดังนั้นสิ่งนี้เกิดขึ้นในทุก ๆ การหมุนหรือไม่
VicVu

สวัสดี มันเกิดขึ้นเพียงเล็กน้อย แต่เมื่อมันเกิดขึ้นมันจะทำให้แอพเสียชีวิต
KarolDepka

ขอบคุณสำหรับลิงค์ไปยังข้อบกพร่อง คนแรกดูเหมือนจะมีความเกี่ยวข้องมากกว่าคนที่สอง
KarolDepka

1
ใช่ฉันคิดว่าทางออกที่ดีที่สุดของคุณคือไม่อนุญาตให้มีการเปลี่ยนแปลงมุมมองรายการในระหว่างการหมุน
VicVu

1
หากคุณสามารถทำซ้ำได้อย่างง่ายดายฉันขอแนะนำให้พิมพ์ค่าสำหรับ 'getItemCount' ก่อนการโทรทั้งหมดเพื่อ 'แจ้งเตือน *' ... คุณอาจพบว่าจำนวนรายการของคุณไม่ตรงกับสมมติฐานของคุณ
รวย Ehmer

คำตอบ:


209

ฉันมีปัญหาที่เกี่ยวข้อง (อาจ) - การเข้าสู่อินสแตนซ์ใหม่ของกิจกรรมด้วย RecyclerView แต่ด้วยอแด็ปเตอร์ขนาดเล็กก็ทำให้เกิดความผิดพลาดนี้สำหรับฉัน

RecyclerView.dispatchLayout()mRecycler.clearOldPositions()สามารถพยายามที่จะดึงรายการจากเศษก่อนที่จะเรียก สิ่งที่ตามมาคือมันดึงไอเท็มจากพูลทั่วไปที่มีตำแหน่งสูงกว่าขนาดอะแด็ปเตอร์

โชคดีที่มันใช้งานได้ก็ต่อเมื่อPredictiveAnimationsเปิดใช้งานดังนั้นโซลูชันของฉันคือ subclass GridLayoutManager( LinearLayoutManagerมีปัญหาเดียวกันและ 'แก้ไข') และแทนที่supportsPredictiveItemAnimations()เพื่อส่งคืนค่าเท็จ:

/**
 * 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
สิ่งนี้ใช้ได้กับฉันและการปิดใช้งานภาพเคลื่อนไหวที่คาดเดาไม่ได้ทำให้คุณสูญเสียอนิเมชั่นด้วยกัน ไชโย
Robert Liberatore

8
ขอบคุณมาก ๆ ท่าน! ทำงานได้ทันทีด้วย LinearLayoutManager ซึ่งอาจช่วยฉันได้หลายวัน
levavare

8
ขอบคุณมาก. โซลูชั่นนี้ทำงานร่วมกับ LinearLayoutManager
Pruthviraj

8
ผมคิดว่าผู้ชายคนนี้สมควรที่เราสร้างรูปปั้นในเกียรติของความช่วยเหลือที่มีค่าของเขา ... มันเป็นหนึ่งในปัญหาเอกสารที่เลวร้ายที่สุดในเว็บ แต่ดูเหมือนว่าจำนวนมาก dev พบปัญหานี้ ... ฉันสงสัยว่าคุณไม่ได้พบว่ามันจะทำได้ ถูกข้ามไปหาก PredictiveAnimations เป็นเท็จ @KasHunt? เพราะสแต็คเทรซไม่ชัดเจนมาก ...
PAD

4
ใครรู้วิธีการแก้ไขที่ไม่มีแฮ็คนี้? เนื่องจากการแจ้งเตือนชุดข้อมูลการเปลี่ยนแปลงถูกปล่อยในความโปรดปรานของ DiffUtil
Anton Shkurenko

83

ในกรณีของฉัน (ลบ / แทรกข้อมูลในโครงสร้างข้อมูลของฉัน) ฉันต้องการล้างกลุ่มรีไซเคิลและแจ้งชุดข้อมูลที่เปลี่ยนแปลง!

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


6
ปกติฉันไม่ได้พูดแบบนี้ แต่ขอบคุณมาก ฉันพยายามทุกอย่างเพื่อแก้ไขข้อผิดพลาดนี้ซึ่งเกิดขึ้นเป็นระยะเมื่อฉันย้ายรายการจำนวนมากในรายการโดยเรียงตามลำดับอย่างรวดเร็ว ฉันใช้เวลาทั้งสัปดาห์ในการพยายามแก้ไขปัญหานี้ ฉันเดินจากมันไปสองสามเดือนเพื่อให้สมองของฉันมีโอกาสที่จะเข้าใกล้มันแตกต่างจากนั้นก็พบสิ่งนี้ในความพยายามครั้งแรกของ Google อวยพรคุณ!
Chantell Osejo

ฉันขอคุกกี้ให้คุณหนึ่งอันเพราะคุณสมควรได้รับคุกกี้ทุกชิ้น ขอบคุณ.
antonis_st

ทำไมคุณต้องทำเช่นนี้?
dabluck

9
นั่นเป็นการดำเนินงานที่ค่อนข้างหนักซึ่งเอาชนะจุดประสงค์ของการรีไซเคิลมุมมอง
gjsalot

@gjsalot ดังนั้นถ้าฉันใช้สิ่งนี้มันจะทำให้เกิดปัญหาได้ไหม?
Sreekanth Karumanaghat

38

ใช้notifyDataSetChanged()แทนnotifyItem...ในกรณีนี้


5
ในบางกรณีนี่คือวิธีที่จะไป ฉันมีสถานการณ์ที่ฉันแทนที่รายการทั้งหมดของฉัน แต่ฉันไม่ได้ซื่อสัตย์กับอะแดปเตอร์เพียงบอกว่าฉันได้แทรกรายการใหม่บางอย่าง (alertItemRangeInserted แจ้ง) โดยไม่บอกก่อนว่าฉันได้ลบรายการ อะแดปเตอร์คาดว่าจะมีรายการมากกว่านั้นจริง ๆ แล้วมี หากใช้วิธีการแจ้งเตือนใด ๆ ของอแด็ปเตอร์คาดว่า NOTEDataSetChanged เช่น alertItemRangeRemoved / Inserted / Updated ผู้เรียกมีหน้าที่รับผิดชอบอย่างเต็มที่ในการแจ้งให้อะแดปเตอร์ทราบว่ามีการเปลี่ยนแปลงอะไรหรือคุณอาจจบด้วย "สถานะที่ไม่สอดคล้องกัน"
JHH

19
นี่ไม่ใช่วิธีแก้ปัญหาเลย
Miha_x64

นี่ไม่ใช่วิธีที่จะไป หากวิธีนี้ใช้งานได้หมายความว่าคุณเพิ่งทำช่วงnotifyItem...และแก้ไขให้ถูกต้องซึ่งจะเริ่มทำงานแทนการเรนเดอร์รายการทั้งหมดอีกครั้ง
Ranjan

12

ฉันแก้ไขปัญหานี้ได้โดยการล่าช้าmRecycler.setAdapter(itemsAdapter)จนกระทั่งเพิ่มรายการทั้งหมดลงในอะแดปเตอร์ด้วยmRecycler.addAll(items)และใช้งานได้ ไม่รู้เลยว่าทำไมฉันถึงเริ่มต้นด้วยมันมาจากรหัสของห้องสมุดที่ฉันดูและเห็นบรรทัดเหล่านั้นใน "ลำดับที่ไม่ถูกต้อง" ฉันค่อนข้างแน่ใจว่ามันเป็นแบบนั้นโปรดถ้าใครสามารถยืนยันได้ว่าทำไมมันถึงอธิบาย ดังนั้น? ไม่แน่ใจว่านี่เป็นคำตอบที่ถูกต้องหรือไม่


ฉันคิดว่านี่เป็นวิธีแก้ปัญหาเมื่อฉันล่าช้าอะแดปเตอร์มันก็ดีฉันเชื่อว่า ... ตอนนี้มันปรากฏขึ้นเมื่อตั้งค่าอะแดปเตอร์ในหัวข้อ UI และเพิ่มรายการไป
EngineSense

18
ฉันใช้swapAdapter(adapter, true)แทนsetAdapter(adapter)และมันช่วย
frangulyan

11

ฉันมีปัญหาที่คล้ายกัน แต่ไม่เหมือนกันทั้งหมด ในกรณีของฉันที่ 1 จุดฉันได้ล้างอาเรย์ที่ถูกส่งไปยัง recyclerview

mObjects.clear();

และไม่โทรไปที่ InformDataSetChanged เนื่องจากฉันไม่ต้องการให้ recyclerview ลบมุมมองทันที ฉันเติมอาร์เรย์ mObjects อีกครั้งใน AsyncTask


9

ฉันมีปัญหาเดียวกันกับ recyclerView ดังนั้นฉันเพิ่งแจ้งอะแดปเตอร์เกี่ยวกับการเปลี่ยนแปลงชุดข้อมูลทันทีหลังจากล้างรายการ

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

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

1
ความผิดพลาดง่าย ๆ นี้ทำให้ฉันเสียเวลามากขอบคุณมาก!
leb1755

7

ฉันมีปัญหาเดียวกันมันเกิดขึ้นเมื่อฉันเลื่อนเร็วและเรียก API และอัปเดตข้อมูลหลังจากพยายามทุกอย่างเพื่อป้องกันความผิดพลาดฉันพบวิธีแก้ไข

mRecyclerView.stopScroll();

มันจะทำงาน.


นี่เป็นวิธีแก้ปัญหาไม่ใช่การแก้ไข คุณกำลังบังคับให้หยุดการเลื่อน Bad UX
Dr. aNdRO

1
@ Dr.aNdRO: อะแดปเตอร์จำเป็นต้องตั้งค่าตำแหน่งและหากคุณยังคงเลื่อน recylerview อะแดปเตอร์ไม่สามารถตั้งค่าข้อมูลซึ่งเป็นสาเหตุของความผิดพลาด มันไม่เลวเลย UX
Anand Savjani

1
เข้าท่า การหยุดการเลื่อนไม่ได้เป็นค่าใช้จ่ายที่แย่เนื่องจากการรีเฟรชข้อมูลเกิดขึ้น
Sush

6

ฉันกำลังแก้ไขข้อมูลสำหรับในพื้นหลังRecyclerView Threadฉันได้รับเช่นExceptionเดียวกับ OP ฉันเพิ่มสิ่งนี้หลังจากเปลี่ยนข้อมูล:

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

หวังว่ามันจะช่วย


ขอบคุณคน! นี่เป็นคำตอบเดียวที่เข้าใจได้จากมุมมองการพัฒนา Android
user347187

ในขณะที่ฉันยังมีการแก้ไขด้วยความช่วยเหลือของฉันใช้view.recycler_view.post notifyItemInsertedในกรณีของฉันมันเป็นเธรด UI แล้ว
CoolMind

6

ข้อผิดพลาดนี้เกิดขึ้นเมื่อรายการอะแดปเตอร์ชัดเจนเมื่อใช้การเลื่อนที่ทำให้ตำแหน่งของผู้ถือรายการเปลี่ยนแปลงโทษที่หายไประหว่างรายการและรายการบน UI ข้อผิดพลาดเกิดขึ้นในครั้งต่อไป "notifyDataSetChanged"คำขอ

การแก้ไข:

ตรวจสอบวิธีรายการอัปเดตของคุณ ถ้าคุณทำสิ่งที่ชอบ

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

===> Error occur

วิธีแก้ไข สร้างรายการวัตถุใหม่สำหรับการประมวลผลบัฟเฟอร์และกำหนดอีกครั้งให้กับรายการหลักหลังจากนั้น

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

ขอบคุณNhan Caoสำหรับความช่วยเหลือที่ยอดเยี่ยมนี้ :)


4

ปัญหาของฉันหายไปหลังจากฉันปรับเปลี่ยนAdapterการใช้งานของฉันเพื่อใช้สำเนาของแถวลำดับรายการแทนที่จะเป็นข้อมูลอ้างอิง วิธีการที่เรียกว่าทุกครั้งที่เรามีรายการใหม่ที่จะแสดงในsetItems()RecyclerView

แทน:

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

    (....)

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

ฉันทำ:

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

วิธีนี้จะช่วยแก้ปัญหาได้ แต่จะใช้หน่วยความจำดั้งเดิมสองครั้งหรือไม่
Sreekanth Karumanaghat

@ MiguelA.Gabriel สิ่งนี้จะส่งผลต่อประสิทธิภาพหรือไม่ เช่นในกรณีของฉันฉันกำลังอัปเดตอาร์เรย์ของ recylerview บ่อยเกินไปดังนั้นขณะนี้ฉันกำลังทำสิ่งนี้suggestionsRecyclerView.swapAdapter(new CandidatesAdapter(mSuggestions), true); และนี่คือตัวสร้างของฉัน public CandidatesAdapter(List<String> suggestionsList) { this.suggestionsList = new ArrayList<>(suggestionsList); }
Mateen Chaudhry

@ mateen-chaudhry มันอาจจะเป็นไปได้ คุณจะต้องทดสอบในกรณีของคุณและตัดสินใจหรือลองใช้วิธีแก้ไขปัญหาอื่นที่เสนอ อย่างที่ฉันพูดมันเป็นเพียงวิธีแก้ปัญหาและใช้งานได้ในกรณีของฉัน
Miguel A. Gabriel

3

ฉันต้องเผชิญกับสถานการณ์เดียวกัน และมันก็แก้ไขได้โดยการเพิ่มรหัสก่อนที่คุณจะล้างคอลเลกชันของคุณ

mRecyclerView.getRecycledViewPool().clear();


3

ในกรณีของฉันฉันกำลังอัพเดตไอเท็มและเรียกใช้notifyDataSetChangedในเธรดที่ไม่ใช่ UI ส่วนใหญ่เวลาทำงาน แต่เมื่อมีการเปลี่ยนแปลงเกิดขึ้นอย่างรวดเร็วมันจะพัง เมื่อฉันทำแทนโดยทั่วไป

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

จากนั้นก็หยุดการทำงานล้มเหลว


3

คุณจะต้องล้างรายการของคุณOnPostExecute()และไม่ทำในขณะที่Pull 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

            }
        });

ผมค้นพบว่าสิ่งนี้เกิดขึ้นเมื่อคุณเลื่อนระหว่างการดึงเพื่อรีเฟรชตั้งแต่ฉันได้รับการล้างรายการก่อนที่async taskผลการ java.lang.IndexOutOfBoundsException: Inconsistency detected.

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

ด้วยวิธีนี้คุณจะไม่จบด้วยความไม่ลงรอยกัน


2

นอกจากนี้ยังสามารถเกี่ยวข้องกับการตั้งค่าการ์ดเชื่อมต่อได้หลายครั้งในเวลาเดียวกัน ฉันมีวิธีการโทรกลับซึ่งเรียกใช้ 5-6 ครั้งในเวลาเดียวกันและฉันตั้งค่าอะแดปเตอร์ในการโทรกลับนั้นดังนั้น RecycledViewPool จึงไม่สามารถจัดการกับข้อมูลเหล่านั้นทั้งหมดได้ในเวลาเดียวกัน เป็นโอกาสที่อ้วน แต่คุณควรตรวจสอบดู


1
ใช่ปัญหาเดียวกัน .. แต่แก้ปัญหา? คุณให้เหตุผลเท่านั้น .. วิธีการแก้ไข?
Ranjith Kumar

@RanjithKumar โปรดแบ่งปันวิธีการของคุณเพื่อแก้ไขปัญหาข้างต้น ฉันแก้ไขมันโดยใช้ mRecyclerView.getRecycledViewPool (). clear (); ก่อนที่จะ notifyDataSetChanged และการใช้บล็อกตรงกันรอบ ๆ ฟังก์ชั่นการปรับปรุงของอะแดปเตอร์
Attiq ur Rehman

คุณช่วยกรุณาดูรหัสของฉันฉันคิดว่าปัญหาของฉันเป็นเหมือนของคุณคุณสามารถช่วยฉันstackoverflow.com/questions/50213362/ …
Mateen Chaudhry

2

ใช้

notifyDataSetChanged()

แทน

notifyItemRangeInserted(0, YourArrayList.size())

ในกรณีนี้.


1
แต่สิ่งนี้ไม่ดีสำหรับประสิทธิภาพใช่ไหม notifyItemRangeInserted จะดีกว่าปัญหาไม่ได้อยู่ที่นี่
Derekyy

2

สำหรับการแก้ไขปัญหานี้เพียงโทรแจ้งให้ทราบ DataSetChanged () พร้อมรายการว่างก่อนที่จะอัปเดตมุมมองรีไซเคิล

ตัวอย่างเช่น

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear (); // รายการสำหรับมุมมองรีไซเคิลรีไซเคิล

adapter.notifyDataSetChanged();

2
ไม่ใช่ทางออก
Miha_x64

@Milha ฉันไม่ได้รับการแก้ไขอื่น ๆ เพื่อแก้ไขปัญหาความผิดพลาด แต่วิธีแก้ปัญหาข้างต้นนั้นใช้ได้สำหรับฉัน หากไม่ใช่วิธีการแก้ไขให้บอกวิธีแก้ไขที่เหมาะสมให้ฉัน
EKN

มันขึ้นอยู่กับ. คุณสามารถลองใช้ DiffUtil - เครื่องมืออเนกประสงค์สำหรับการอัพเดตเนื้อหา RecyclerView
Miha_x64

2

ในกรณีของฉันฉันเพิ่งลบบรรทัดด้วย setHasStableIds(true);


แต่ HasStableIds (จริง) ช่วยปรับปรุงประสิทธิภาพของ Rv มีวิธีการแก้ปัญหาอื่นหรือไม่?
Sreekanth Karumanaghat

ที่จริงฉันคิดว่าอาจเป็นเพราะเหตุผลที่แตกต่างกันดังนั้นอาจมีวิธีแก้ไขปัญหาต่าง ๆ ตามสาเหตุที่แท้จริง
Sreekanth Karumanaghat

2

ในกรณีของฉันฉันกำลังพยายามเปลี่ยนเนื้อหาอะแดปเตอร์ของฉันบนเธรดพื้นหลัง แต่เรียกว่าแจ้งเตือน * บนเธรดหลัก / ui

นั่นเป็นไปไม่ได้! เหตุผลที่การแจ้งเตือนถูกบังคับให้เธรดหลักคือ recyclerview ต้องการให้คุณแก้ไขอะแด็ปเตอร์สำรองของคุณบนเธรดหลักแม้จะอยู่ในสแต็กการโทรเดียวกัน

ในการแก้ปัญหาตรวจสอบให้แน่ใจว่าทุกการดำเนินการกับอะแดปเตอร์ของคุณเช่นเดียวกับทุก ๆ การแจ้งเตือน ... การโทรถูกทำบนUI / เธรดหลัก !


2
การเพิ่มรายการลงในรายการในอะแดปเตอร์ของคุณควรทำบนเธรดพื้นหลังและโทรแจ้งเตือนบนไปรษณีย์ การเพิ่มข้อมูลในเธรด ui ทำให้แอปหยุดทำงานเป็นเวลามิลลิวินาทีหรือวินาทีหากเพิ่มข้อมูลจำนวนมาก
dione llorera

เห็นด้วยกับ @dionellorera มันควรจะชัดเจนว่า "การเปลี่ยนแปลงในเนื้อหาของอะแดปเตอร์" โดยเฉพาะหมายถึงการปรับเปลี่ยนข้อมูลใด ๆ โดยตรงไม่ว่าจะเป็นค่าดั้งเดิมคุณสมบัติของวัตถุหรือวัตถุตัวเอง
OzzyTheGiant

2

ฉันวิ่งเข้าไปในกองติดตามที่น่ารังเกียจนี้กับส่วนประกอบสถาปัตยกรรม Android ใหม่เมื่อเร็ว ๆ นี้ โดยพื้นฐานแล้วฉันมีรายการของรายการใน ViewModel ซึ่งตรวจพบโดย Fragment ของฉันโดยใช้ LiveData เมื่อ ViewModel โพสต์ค่าใหม่สำหรับข้อมูล Fragment จะอัปเดตอะแดปเตอร์ส่งผ่านองค์ประกอบข้อมูลใหม่เหล่านี้และแจ้งให้อะแดปเตอร์ทราบว่ามีการเปลี่ยนแปลง

น่าเสียดายที่เมื่อผ่านองค์ประกอบข้อมูลใหม่ไปยังอะแดปเตอร์ฉันล้มเหลวในการพิจารณาข้อเท็จจริงที่ว่าทั้ง ViewModel และอะแดปเตอร์จะชี้ไปที่การอ้างอิงวัตถุเดียวกัน! หมายความว่าหากฉันอัปเดตข้อมูลและการโทรpostValue()จากภายใน ViewModel มีหน้าต่างเล็ก ๆ ที่สามารถอัปเดตข้อมูลได้และอะแดปเตอร์ยังไม่ได้รับการแจ้งเตือน!

การแก้ไขของฉันคือการยกตัวอย่างองค์ประกอบใหม่เมื่อส่งผ่านไปยังอะแดปเตอร์:

mList = new ArrayList<>(passedList);

ด้วยการแก้ไขที่ง่ายสุดนี้คุณสามารถมั่นใจได้ว่าข้อมูลอะแดปเตอร์ของคุณจะไม่เปลี่ยนแปลงจนกว่าจะถึงเวลาก่อนที่อะแดปเตอร์ของคุณจะได้รับแจ้ง


2

นี่เป็นเพียงวิธีการแก้ปัญหาที่ใช้งานได้สำหรับฉันแม้จะลองใช้วิธีแก้ไขปัญหาด้านบนมากมาย

1. ) ความตั้งใจ

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

2. ) เขียนวิธีนี้ในอะแดปเตอร์

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

stockListModels -> รายการนี้เป็นรายการที่คุณใช้ในอะแดปเตอร์


2

สำหรับฉันมันใช้งานได้หลังจากเพิ่มรหัสบรรทัดนี้:

mRecyclerView.setItemAnimator(null);

2
นี่ไม่ใช่การแก้ไขในกรณีส่วนใหญ่หากคุณต้องการภาพเคลื่อนไหวคุณได้เขียนรหัสอะแดปเตอร์ของคุณใหม่และค้นหาข้อผิดพลาดในการแจ้งเตือนการเปลี่ยนแปลง
Dragos Rachieru

มันใช้งานได้ดีสำหรับฉันฉันใช้อะแดปเตอร์จริงดังนั้นฉันไม่สามารถควบคุมโฟลว์และฉันเปิดใช้งาน windowActivityTransitions ในสไตล์ที่ทำให้เกิดปัญหานี้ขอบคุณผู้ชายที่คุณช่วยชีวิตฉันไว้
Arul Mani

1

ปัญหานี้อาจเกิดขึ้นเมื่อคุณลองล้างรายการของคุณถ้าคุณจะล้างรายการข้อมูลของคุณโดยเฉพาะอย่างยิ่งเมื่อคุณใช้การดึงเพื่อรีเฟรชพยายามใช้แฟล็กบูลีนเริ่มต้นมันเป็นเท็จและภายในวิธีการ OnRefresh ทำให้เป็นจริง หากการตั้งค่าสถานะเป็นจริงก่อนเพิ่มข้อมูลใหม่ลงในนั้นและหลังจากนั้นทำให้เป็นเท็จ

รหัสของคุณอาจเป็นแบบนี้

 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

ฉันมีปัญหาเดียวกันก่อนหน้านี้ ในที่สุดก็พบวิธีแก้ปัญหาสำหรับสิ่งนั้น

สิ่งที่ฉันต้องทำคือแจ้งให้อะแดปเตอร์ทราบว่ารายการนั้นถูกลบออกแล้วแจ้งช่วงของชุดข้อมูลอะแดปเตอร์เปลี่ยนไป

 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

ฉันพบปัญหาที่คล้ายกันและเพิ่งคิดออก ฉันฮาร์ดโค้ดตัวอย่างสำหรับกรณีทดสอบ แต่ไม่แน่ใจว่าพวกเขาแต่ละคนส่งคืน ID ที่ไม่ซ้ำกันและทำให้เกิดความผิดพลาดด้านล่างสำหรับฉัน การแก้ไขรหัสแก้ไขปัญหาหวังว่าจะช่วยคนอื่นได้!


1

ฉันเคยได้รับข้อผิดพลาดด้วย:

สาเหตุ:ฉันพยายามอัปเดตมุมมอง Recycler จากงาน Async ในขณะที่พยายามรับ viewHolders ที่ถูกลบไปพร้อม ๆ กัน

รหัส:ฉันสร้างข้อมูลที่กดปุ่มตรรกะดังนี้

  1. ล้างรายการสุดท้ายในมุมมองรีไซเคิล
  2. เรียกใช้งาน async เพื่อสร้างข้อมูล
  3. OnPostExecute อัปเดตมุมมอง Recycler และ NotifyDataSetChanged

ปัญหา:เมื่อใดก็ตามที่ฉันเลื่อนเร็วก่อนที่จะสร้างข้อมูลของฉันฉันจะได้รับ

ตรวจพบความไม่สอดคล้องกัน ตำแหน่งของการ์ดเชื่อมต่อ view viewHolder java.lang.IndexOutOfBoundsException: ตรวจพบความไม่สอดคล้องกัน ตำแหน่งรายการไม่ถูกต้อง 20 (ชดเชย: 2). สถานะ: 3

โซลูชัน:แทนที่จะล้างข้อมูล RecyclerView ก่อนสร้างข้อมูลของฉันฉันปล่อยไว้แล้วแทนที่ด้วยข้อมูลใหม่การแจ้งเตือนการโทรเปลี่ยนชุดข้อมูลที่แสดงด้านล่าง

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

คุณช่วยลองดูรหัสของฉันฉันคิดว่าปัญหาของฉันเป็นเหมือนคุณ [link] ( stackoverflow.com/questions/50213362/ … )
Mateen Chaudhry

1

เพียงลบมุมมองทั้งหมดของเครื่องมือจัดการการออกแบบของคุณออกก่อนแจ้งเตือน ชอบ:

myLayoutmanager.removeAllViews();

มันได้ผล. ฉันมีปัญหากับการโหลดแบบเลื่อนและการเปลี่ยนแท็บ
Warwicky

1

ใช้ListAdapter (androidx.recyclerview.widget.ListAdapter)โทรadapter.submitList(null)ก่อนโทรadapter.submitList(list):

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

1

ข้อยกเว้นนี้เกิดขึ้นกับ API 19, 21 (แต่ไม่ใช่ใหม่) ใน Kotlin coroutine ฉันโหลดข้อมูล (ในเธรดพื้นหลัง) และในเธรด UI ที่เพิ่มและแสดง:

adapter.addItem(item)

อะแดปเตอร์:

var list: MutableList<Item> = mutableListOf()

init {
    this.setHasStableIds(true)
}

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

ด้วยเหตุผลบางอย่างที่ Android ไม่สามารถแสดงผลได้เร็วพอหรืออย่างอื่นดังนั้นฉันจึงอัปเดตรายการในรูปpostแบบของRecyclerView(เพิ่มลบอัปเดตกิจกรรมของรายการ):

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

ข้อยกเว้นนี้คล้ายกับ "ไม่สามารถเรียกวิธีนี้ในการเรียกกลับแบบเลื่อนการเรียกกลับแบบเลื่อนอาจทำงานในระหว่างการวัดและเลย์เอาต์ที่คุณไม่สามารถเปลี่ยนข้อมูล RecyclerView ได้การเรียกวิธีใด ๆ ที่อาจเปลี่ยนโครงสร้างของ RecyclerView หรือเนื้อหาของอะแด็ปเตอร์ เฟรมถัดไป ":. Recyclerview - ไม่สามารถเรียกวิธีการนี้ในการเรียกกลับเลื่อน


0

ฉันพบว่าการตั้งค่า mRecycler.setLayoutFrozen (จริง); ในเมธอด onRefresh ของ swipeContainer

แก้ไขปัญหาให้ฉัน

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

        }
    });

0

นี่เป็นข้อผิดพลาดที่ค่อนข้างน่ารังเกียจ

เพื่อจัดการกับการคลิกไอเท็มของฉันฉันใช้การนำไปใช้ที่RecyclerView.OnItemTouchListenerคล้ายกับโซลูชันที่พบในคำถามนี้

หลังจากรีเฟรชRecyclerViewแหล่งข้อมูลของหลาย ๆ ครั้งแล้วคลิกรายการสิ่งนี้IndexOutOfBoundsExceptionจะทำให้แอปพลิเคชันของฉันทำงานล้มเหลว เมื่อมีการคลิกไอเท็มRecyclerViewภายในจะมองหามุมมองที่ถูกต้องและให้ตำแหน่งกลับคืน ตรวจสอบซอร์สโค้ดฉันเห็นว่ามีบางอย่างTasksและThreadsกำหนดเวลาไว้ ในการตัดเรื่องให้สั้นโดยทั่วไปเป็นเพียงบางรัฐที่ผิดกฎหมายที่มีแหล่งข้อมูลสองแหล่งผสมและไม่ซิงโครไนซ์

จากสิ่งนี้ฉันได้ลบการใช้งานRecyclerView.OnItemTouchListenerและเพียงแค่คลิกViewHolderที่Adapterตัวเอง:

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

}

นี่อาจไม่ใช่ทางออกที่ดีที่สุด แต่ตอนนี้ปลอดความผิดพลาดหวังว่านี่จะช่วยคุณประหยัดเวลา :)


การสร้างวัตถุใหม่ทุกครั้งที่มีการเรียกใช้ผูกจะส่งผลให้มีวัตถุจำนวนมากที่จะถูกรวบรวมขยะและผู้ใช้อาจพบว่าการแช่แข็ง
dephinera
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.