Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inconsistency detected. Invalid view holder adapter positionViewHolder

I am getting the above mention crash in a few devices lately, don't know what is triggering it.

Some answers suggest to extend the LayoutManager and silence the exception but I'd rather find a permanent and efficient solution for this, some suggest to clear the list in the adapter which I'm doing but still a crash.

Here's the Adapter class -

class Adapter(private val adapterType: Int, private val clickListener: GalleryHolder.ClickListener) : SelectableAdapter<Adapter.GalleryHolder>() {
    val objectArrayList: ArrayList<String> = ArrayList()
    private var emptyView: View? = null

    companion object {
        const val IMAGE = 0
        const val VIDEO = 1
    }

    fun addAll(items: ArrayList<String>) {
        objectArrayList.addAll(items)
        notifyItemRangeInserted(0, objectArrayList.size)
    }

    fun clear() {
        val size = objectArrayList.size
        notifyItemRangeRemoved(0, size)
        objectArrayList.clear()
    }

    override fun getItemCount(): Int {
        return objectArrayList.size
    }

    fun setEmptyView(emptyView: View) {
        this.emptyView = emptyView
    }

    fun toogleEmptyView() {
        if (objectArrayList.size == 0) emptyView!!.visibility = View.VISIBLE
        else emptyView!!.visibility = View.GONE
    }


    fun removeItems(positions: MutableList<Int>) {
        positions.sortWith(Comparator { lhs, rhs -> rhs!! - lhs!! })

        while (positions.isNotEmpty()) {
            if (positions.size == 1) {
                removeItem(positions[0])
                positions.removeAt(0)
            } else {
                var count = 1
                while (positions.size > count && positions[count] == positions[count - 1] - 1)
                    ++count

                if (count == 1)
                    removeItem(positions[0])
                else
                    removeRange(positions[count - 1], count)

                if (count > 0) positions.subList(0, count).clear()
            }
        }

        toogleEmptyView()
    }

    private fun removeRange(positionStart: Int, itemCount: Int) {
        for (i in 0 until itemCount) objectArrayList.removeAt(positionStart)
        notifyItemRangeRemoved(positionStart, itemCount)
    }

    private fun removeItem(position: Int) {
        objectArrayList.removeAt(position)
        notifyItemRemoved(position)
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GalleryHolder {
        var layout = R.layout.item
        if (adapterType == VIDEO) layout = R.layout.item_video

        val view = LayoutInflater.from(parent.context).inflate(layout, parent, false)
        return GalleryHolder(view)
    }

    override fun onBindViewHolder(holder: GalleryHolder, position: Int) {
        if (adapterType == VIDEO)
            Glide.with(holder.img)
                    .asBitmap()
                    .apply(RequestOptions()
                            .frame(TimeUnit.SECONDS.toMicros(2))
                            .diskCacheStrategy(DiskCacheStrategy.ALL))
                    .load(objectArrayList[holder.adapterPosition])
                    .transition(BitmapTransitionOptions.withCrossFade())
                    .into(holder.img)
        else
            Glide.with(holder.img)
                    .asBitmap()
                    .apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL))
                    .load(objectArrayList[holder.adapterPosition])
                    .transition(BitmapTransitionOptions.withCrossFade())
                    .into(holder.img)

        // Highlight the item if it's selected
        holder.selectedOverlay.setBackgroundColor(ColorUtils.setAlphaComponent(HelperMethods.getPrimaryColor(holder.selectedOverlay.context), 175))
        holder.selectedOverlay.visibility = if (isSelected(holder.adapterPosition)) View.VISIBLE else View.INVISIBLE
        holder.itemView.setOnClickListener { clickListener.onItemClicked(holder.adapterPosition) }
        holder.itemView.setOnLongClickListener { clickListener.onItemLongClicked(holder.adapterPosition) }
    }

    class GalleryHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
        internal val img: ImageView = itemView.findViewById(R.id.img)
        internal val selectedOverlay: RelativeLayout = itemView.findViewById(R.id.parent)

        interface ClickListener {
            fun onItemClicked(position: Int)
            fun onItemLongClicked(position: Int): Boolean
        }
    }
}

Following is the stacktrace -

Fatal Exception: java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{41bc02f0 position=116 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached not recyclable(1) no parent} androidx.recyclerview.widget.RecyclerView{41dc3c18 VFED.... .F....ID 0,0-480,606 #7f090125 app:id/recycler_view}, adapter:c.g.a.b.a@41df0ba8, layout:androidx.recyclerview.widget.GridLayoutManager@41d8ca08, context:com.example.MainActivity@41cc4090
   at androidx.recyclerview.widget.RecyclerView$Recycler.setViewCacheExtension(RecyclerView.java:79)
   at androidx.recyclerview.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition + 5901(RecyclerView.java:5901)
   at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline + 6084(RecyclerView.java:6084)
   at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition + 6044(RecyclerView.java:6044)
   at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition + 6040(RecyclerView.java:6040)
   at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next + 2303(LinearLayoutManager.java:2303)
   at androidx.recyclerview.widget.GridLayoutManager.layoutChunk + 561(GridLayoutManager.java:561)
   at androidx.recyclerview.widget.LinearLayoutManager.onAnchorReady(LinearLayoutManager.java:105)
   at androidx.recyclerview.widget.LinearLayoutManager.fill + 1587(LinearLayoutManager.java:1587)
   at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren + 665(LinearLayoutManager.java:665)
   at androidx.recyclerview.widget.GridLayoutManager.onLayoutChildren + 170(GridLayoutManager.java:170)
   at androidx.recyclerview.widget.RecyclerView.onScrollStateChanged(RecyclerView.java:74)
   at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1 + 4014(RecyclerView.java:4014)
   at androidx.recyclerview.widget.RecyclerView.dispatchLayout + 3778(RecyclerView.java:3778)
   at androidx.recyclerview.widget.RecyclerView.onLayout + 4333(RecyclerView.java:4333)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.RelativeLayout.onLayout + 1055(RelativeLayout.java:1055)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at androidx.swiperefreshlayout.widget.SwipeRefreshLayout.onLayout + 625(SwipeRefreshLayout.java:625)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.FrameLayout.layoutChildren + 453(FrameLayout.java:453)
   at android.widget.FrameLayout.onLayout + 388(FrameLayout.java:388)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at androidx.viewpager.widget.ViewPager.onLayout + 1775(ViewPager.java:1775)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.LinearLayout.setChildFrame + 1677(LinearLayout.java:1677)
   at android.widget.LinearLayout.layoutVertical + 1531(LinearLayout.java:1531)
   at android.widget.LinearLayout.onLayout + 1440(LinearLayout.java:1440)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at androidx.drawerlayout.widget.DrawerLayout.onLayout + 1231(DrawerLayout.java:1231)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.FrameLayout.layoutChildren + 453(FrameLayout.java:453)
   at android.widget.FrameLayout.onLayout + 388(FrameLayout.java:388)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.FrameLayout.layoutChildren + 453(FrameLayout.java:453)
   at android.widget.FrameLayout.onLayout + 388(FrameLayout.java:388)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.FrameLayout.layoutChildren + 453(FrameLayout.java:453)
   at android.widget.FrameLayout.onLayout + 388(FrameLayout.java:388)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.LinearLayout.setChildFrame + 1677(LinearLayout.java:1677)
   at android.widget.LinearLayout.layoutVertical + 1531(LinearLayout.java:1531)
   at android.widget.LinearLayout.onLayout + 1440(LinearLayout.java:1440)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.widget.FrameLayout.layoutChildren + 453(FrameLayout.java:453)
   at android.widget.FrameLayout.onLayout + 388(FrameLayout.java:388)
   at android.view.View.layout + 15602(View.java:15602)
   at android.view.ViewGroup.layout + 4880(ViewGroup.java:4880)
   at android.view.ViewRootImpl.performLayout + 2256(ViewRootImpl.java:2256)
   at android.view.ViewRootImpl.performTraversals + 2010(ViewRootImpl.java:2010)
   at android.view.ViewRootImpl.doTraversal + 1234(ViewRootImpl.java:1234)
   at android.view.ViewRootImpl$TraversalRunnable.run + 6301(ViewRootImpl.java:6301)
   at android.view.Choreographer$CallbackRecord.run + 813(Choreographer.java:813)
   at android.view.Choreographer.doCallbacks + 613(Choreographer.java:613)
   at android.view.Choreographer.doFrame + 583(Choreographer.java:583)
   at android.view.Choreographer$FrameDisplayEventReceiver.run + 799(Choreographer.java:799)
   at android.os.Handler.handleCallback + 733(Handler.java:733)
   at android.os.Handler.dispatchMessage + 95(Handler.java:95)
   at android.os.Looper.loop + 146(Looper.java:146)
   at android.app.ActivityThread.main + 5511(ActivityThread.java:5511)
   at java.lang.reflect.Method.invokeNative(Method.java)
   at java.lang.reflect.Method.invoke + 515(Method.java:515)
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run + 1283(ZygoteInit.java:1283)
   at com.android.internal.os.ZygoteInit.main + 1099(ZygoteInit.java:1099)
   at dalvik.system.NativeStart.main(NativeStart.java)

Any help would be appreciated.

like image 277
DarShan Avatar asked Jul 03 '19 07:07

DarShan


2 Answers

The error appears, because the system is trying to find a list element, which is not existent anymore, because you removed it in an earlier loop iteration.

Make sure you do not remove elements from a list while you are still iterating over list items or accessing the list.

Just create a separate list for the elements you want to remove and remove them AFTER the loops are complete.

like image 190
8K Creative Commons Avatar answered Oct 13 '22 02:10

8K Creative Commons


You are trying to notify removed items which are not yet removed.

You need to first clear your list objectArrayList.clear() and then notifyItemRangeRemoved(0, size).

like image 24
Daniele Avatar answered Oct 13 '22 04:10

Daniele