programing

재활용자 보기:불일치가 탐지되었습니다.잘못된 품목 위치

starjava 2023. 8. 30. 21:03
반응형

재활용자 보기:불일치가 탐지되었습니다.잘못된 품목 위치

당사의 QA에서 버그를 감지했습니다: Android 장치(Droid Turbo)를 회전할 때 다음과 같은 RecyclerView 관련 충돌이 발생했습니다.

자바.java.javaIndex Out Of Bounds 예외:불일치가 탐지되었습니다.잘못된 항목 위치 2(오프셋:2). 상태:3

RecyclerView 내부 오류로 보입니다. 코드에 의해 직접 발생할 수 있는 방법은 생각할 수 없습니다.

이 문제가 발생한 사람이 있습니까?

해결책은 무엇일까요?

문제가 발생할 때 예외를 포착하여 손상된 상태로 남지 않도록 RecyclerverView 인스턴스를 처음부터 다시 만드는 것이 매우 어려운 해결 방법일 수 있습니다.

하지만, 가능하다면, 저는 문제를 가리는 대신 문제를 더 잘 이해하고 싶습니다(그리고 아마도 그 근원에서 그것을 고치고 싶습니다).

그 벌레는 번식하기가 쉽지 않지만, 그것이 일어났을 때 치명적입니다.

전체 스택 추적:

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

RecyclerView를 사용하여 활동의 새 인스턴스를 입력하는 것과 관련된 문제가 있었습니다. 그러나 어댑터가 더 작으면 이러한 충돌이 발생했습니다.

RecyclerView.dispatchLayout()를 호출하기 수 있습니다.mRecycler.clearOldPositions()그 결과 어댑터 크기보다 높은 위치를 가진 공통 풀에서 항목을 꺼냈습니다.

다행히도, 이것은 오직 다음과 같은 경우에만 가능합니다.PredictiveAnimations사용하도록 설정되어 있으므로, 제 솔루션은 하위 클래스에 있었습니다.GridLayoutManager(LinearLayoutManager를 가지고 , 재정의합니다.supportsPredictiveItemAnimations()falsefalse를 합니다.

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

내 경우(데이터 구조에 데이터 삭제/삽입) 재활용 풀을 지우고 데이터 집합이 변경되었음을 알려야 했습니다!

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

사용하다notifyDataSetChanged()에 신대notifyItem...이 경우에는

가 있었습니다.RecyclerView그래서 나는 리스트가 삭제된 직후에 어댑터에 데이터 세트 변경에 대해 통보했습니다.

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

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

이 오류는 항목 홀더의 위치를 변경하는 사용자 스크롤 시 어댑터의 목록 지우기, 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님께 감사드립니다 :)

저는 이것을 연기함으로써 해결했습니다.mRecycler.setAdapter(itemsAdapter)에로 모든 항목을 mRecycler.addAll(items)그리고 그것은 성공하였다.제가 왜 그렇게 했는지 모르겠습니다. 제가 도서관의 코드를 보고 "잘못된 순서"에 있는 줄들을 본 것입니다. 저는 이것이 사실이라고 확신합니다. 누군가가 그것이 왜 그런지 설명해 줄 수 있을까요?.

저도 같은 문제가 있어요.빠르게 스크롤하고 API를 호출하여 데이터를 업데이트할 때 발생했습니다.충돌을 막기 위해 모든 것을 시도한 후, 저는 해결책을 찾았습니다.

mRecyclerView.stopScroll();

그건 작동할 것이다.

저도 비슷한 문제가 있었지만 정확히 같은 문제는 아니었습니다.저의 경우, 한 지점에서 재활용 업체 뷰에 전달된 배열을 삭제하고 있었습니다.

mObjects.clear();

그리고 notifyDataSetChanged를 호출하지 않습니다. 재활용 보기가 보기를 즉시 지우는 것을 원하지 않았기 때문입니다.비동기 작업에서 mObjects 배열을 다시 채우는 중이었습니다.

저는 이 코드 라인을 추가한 후에 작동했습니다.

mRecyclerView.setItemAnimator(null);

는 데터를변중다니입의 를 변경하고 있습니다.RecyclerViewThread도 같은 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠExceptionOP 제가 데이터를 한 후 한 내용입니다.데이터를 변경한 후 추가했습니다.

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

도움이 되길 바랍니다.

저 같은 경우에는 물건을 업데이트하고 전화를 걸었습니다.notifyDataSetChanged사용자 인터페이스가 아닌 스레드에서.대부분 효과가 있었지만, 많은 변화가 빠르게 일어나면, 그것은 붕괴될 것입니다.할 때는 기본적으로, 기대으, 본적로내에.

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

그리고는 충돌이 멈췄습니다.

저도 같은 상황에 직면한 적이 있습니다.컬렉션을 삭제하기 전에 코드를 추가하여 해결했습니다.

mRecyclerView.getRecycledViewPool().clear();

는 제가 에 사라졌습니다.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);
}

저 같은 경우에는 그냥 라인을 없앴어요.setHasStableIds(true);

이것은 위의 솔루션에서 많은 것을 시도하는 것조차 저에게 효과가 있었던 유일한 솔루션입니다.

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 -> 이 목록은 어댑터에서 사용 중인 목록입니다.

사용하다

notifyDataSetChanged()

대신

notifyItemRangeInserted(0, YourArrayList.size())

이 경우에는

은 당신의 목록을 삭제하기만 하면 .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 taskjava.lang.IndexOutOfBoundsException: Inconsistency detected.

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

그렇게 하면 당신은 불일치로 끝나지 않을 것입니다.

용사를 합니다.ListAdapter (androidx.recyclerview.widget.ListAdapter)을 부르다adapter.submitList(null)전화하기 adapter.submitList(list):

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

LinearLayoutManager를 확장하고 이 오류를 발견합니다.

public class NoCrashLinearLayoutManager extends LinearLayoutManager {

    public NoCrashLinearLayoutManager(Context context) {
        super(context);
    }

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

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

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (IndexOutOfBoundsException e){
            e.printStackTrace();
        }
    }
}

다음과 같은 방법으로 이 충돌을 재현할 수 있습니다.

  1. 항목 어터목지list.clear()하지 않음)("notify****"라는 이름으로 표시됨)
  2. 순환기 보기를 스크롤합니다.

따라서 이 충돌은 알림 메서드를 호출하지 않고 목록에서 항목을 제거하고 스크롤할 때 발생합니다.

어댑터를 동시에 여러 번 설정하는 것과 관련이 있을 수도 있습니다.동시에 5~6회 트리거된 콜백 방법을 사용하여 RecycleViewPool이 모든 데이터를 동시에 처리할 수 없도록 해당 콜백에 어댑터를 설정했습니다.가능성은 희박하지만 어쨌든 확인해 보는 것이 좋을 것입니다.

통지하기 전에 레이아웃 관리자의 모든 보기를 제거하십시오.예:

myLayoutmanager.removeAllViews();

저의 경우 백그라운드 스레드에서 어댑터 내용을 변경하려다가 메인/UI 스레드에서 알림*을 호출했습니다.

그건 불가능해요!알림이 주 스레드로 강제 설정되는 이유는 동일한 호출 스택에서도 주 스레드의 백업 어댑터를 편집하도록 재활용 보기에서 요청하기 때문입니다.

문제를 해결하려면 어댑터에 대한 모든 작업과 알림을 확인하십시오.UI/메인 스레드에서 호출됩니다!

최근에 새로운 Android Architecture Components를 사용하여 이 불쾌한 스택 추적을 접했습니다.기본적으로 LiveData를 사용하여 fragment에서 관찰되는 항목 목록이 ViewModel에 있습니다.View Model이 데이터에 대한 새 값을 게시하면 Fragment가 어댑터를 업데이트하여 이러한 새 데이터 요소를 전달하고 변경 사항이 있음을 어댑터에 알립니다.

안타깝게도 어댑터에 새 데이터 요소를 전달할 때 View 모델과 어댑터가 모두 동일한 개체 참조를 가리킬 것이라는 사실을 고려하지 못했습니다!그 말은 내가 데이터를 업데이트하고 전화를 하면postValue()View Model(보기 모델) 내에는 데이터를 업데이트할 수 있는 매우 작은 창이 있으며 어댑터에 아직 알림이 표시되지 않습니다!

어댑터에 전달될 때 요소의 새 복사본을 인스턴스화하는 것이 제 해결책이었습니다.

mList = new ArrayList<>(passedList);

이 초간단 수정을 통해 어댑터가 알림을 받기 직전까지 어댑터 데이터가 변경되지 않도록 할 수 있습니다.

이 예외는 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)
}

어떤 이유에서인지 안드로이드가 충분히 빨리 렌더링되지 않거나 다른 것으로 렌더링되지 않기 때문에 목록을 업데이트합니다.postRecyclerView(항목의 이벤트 추가, 제거, 업데이트):

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

이 예외는 "스크롤 콜백에서 이 메서드를 호출할 수 없습니다.RecyclerView 데이터를 변경할 수 없는 측정 및 레이아웃 패스 중에 스크롤 콜백이 실행될 수 있습니다.RecyclerView 또는 어댑터 내용의 구조를 변경할 수 있는 메서드 호출은 다음 프레임으로 연기해야 합니다.": RecyclerView - 스크롤 콜백에서는 이 메서드를 호출할 수 없습니다.

저는 방금 같은 문제를 해결했습니다.는 는나가 .RecyclerView.Adapter와 함께setHasStableIds(true)항목 깜박임을 방지하도록 설정합니다.

나는 복제 가능한 필드를 사용하고 있었습니다.getItemId())id필드):

override fun getItemId(position: Int): Long {
    // Error-prone due to possibly duplicate name.
    return contacts[position].name.hashCode().toLong()
}

getItemId() 에서는 각 항목에 대해 고유한 ID를 반환해야 하므로 솔루션은 다음과 같습니다.

override fun getItemId(position: Int): Long {
    // Contact's phone is unique, so I use it instead.
    return contacts[position].phone.hashCode().toLong()
}

이 문제는 목록을 지우려고 할 때 발생할 수 있습니다. 특히 풀을 사용하여 부울 플래그를 새로 고치려고 할 때 데이터 목록을 지우려면 false로 초기화하고 OnRefresh 메서드 내부에서 false로 만들고 플래그가 새 데이터를 추가하기 직전에 true이면 dataList를 지웁니다.

당신의 코드는 다음과 같을 수 있습니다.

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

}

저도 예전에 같은 문제가 있었습니다.마침내 그것에 대한 해결책을 찾았습니다.

항목이 제거되었음을 어댑터에 알린 다음 어댑터 데이터 집합 범위가 변경되었음을 알리는 작업입니다.

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

 }

저도 비슷한 문제에 부딪혀 이제야 알게 되었습니다.테스트 사례를 위해 몇 가지 예제를 하드 코딩했지만 각 예제가 고유 ID를 반환했는지 확인하지 못했기 때문에 아래와 같은 오류가 발생했습니다.ID를 수정하여 문제가 해결되었습니다. 이것이 다른 사람에게 도움이 되기를 바랍니다!

이 문제를 해결하려면 재활용 보기를 업데이트하기 전에 빈 목록과 함께 알림DataSetChanged()를 호출하십시오.

예를들면

//Method for refresh recycle view

    if (!hcpArray.isEmpty())

hcpArray.clear();//재활용 보기 업데이트 목록

adapter.notifyDataSetChanged();

언급URL : https://stackoverflow.com/questions/30220771/recyclerview-inconsistency-detected-invalid-item-position

반응형