I finished the challenge of deleting a Crime however while the crime gets deleted the app also crashes and I see this StackTrace:
StackTrace
05-24 18:50:32.029 16997-16997/com.bignerdranch.android.criminalintent E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.bignerdranch.android.criminalintent, PID: 16997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{107c5af6 position=3 id=-1, oldPos=3, pLpos:-1 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5251)
at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5433)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5394)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5390)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2149)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1533)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1496)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:593)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3488)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3264)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3798)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:437)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15797)
at android.view.ViewGroup.layout(ViewGroup.java:5198)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2111)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1868)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1086)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6474)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:846)
at android.view.Choreographer.doCallbacks(Choreographer.java:647)
at android.view.Choreographer.doFrame(Choreographer.java:601)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:829)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:977)
at co
I have a feeling that the error is happening because of this line of code in the updateUI() method:
Any time you see the “Inconsistency detected” error, it means that you’ve changed your backing dataset but you haven’t correctly told the RecyclerView about that change. When you delete something, you can use the notifyItemRemoved method.
That said, most people do not bother with the notifyItemChanged/notifyItemRemoved/etc methods because they require a whole lot more work from the developer for very little gain. Most people go straight to notifyDataSetChanged so that they do not have to keep track of exactly what changed. This method will reload all of the views in the RecyclerView that you can see (so ~10-20 rows). If your app does not have heavy rows (facebook for example does have heavy rows), then I would just use notifyDataSetChanged.
Understood. Lets say this app has heavy rows. It sounds like I would need to use some sort of conditional check to decide on the appropriate method to use. I was thinking of having a variable in CrimeListFragment to track the last operation type (Inserted, Changed, Removed). The challenge I have is figuring out how to set this variable. Should I create an intent in CrimeFragment and use getActivity.setResult(…) to send the operation type back to CrimeListFragment?
Do let me know if there is a better way to do this.
In the case where an item was inserted I don’t see the mAdapter.notifyItemChanged(clickedCrimePosition) method throw an Exception
e.g.
if (lastOperationType == ITEM_REMOVED) {
mAdapter.notifyItemRemoved(clickedCrimePosition);
}
if (lastOperationType == ITEM_CHANGED) {
mAdapter.notifyItemChanged(clickedCrimePosition);
}
Without that, you just have to do a lot of manual bookkeeping like what you mentioned. Using the startActivityForResult mechanism is probably how I would do it if I wasn’t using DiffUtil.
@drgnme, that looks correct. I think I must have copied the code for CrimeLab.java from the Chapter 13 solution which did not involve using the SQLite DB. I have corrected my previous statement