I am using FastAdapter on my application to render a list of items, which are read from a remote API.
At the onCreateView
on my fragment, I set up the adapter this way:
FastAdapter<ListModeBaseAdapterItem> fastAdapter = new FastAdapter<>();
itemAdapter = new ItemAdapter<>();
setItemClickListener(fastAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
recyclerView.setAdapter(itemAdapter.wrap(fastAdapter));
recyclerView.setItemAnimator(new SlideDownAlphaAnimator());
recyclerView.getItemAnimator().setAddDuration(ADAPTER_ADD_DURATION);
recyclerView.getItemAnimator().setRemoveDuration(ADAPTER_REMOVE_DURATION);
Then, when new data is loaded, the adapter is updated with this line
public void listModeImageItemsLoaded(List<ListModeBaseAdapterItem> listModeAdapterItems) {
itemAdapter.add(listModeAdapterItems);
}
following is the implementation of ListModeBaseAdapterItem
public abstract class ListModeBaseAdapterItem extends AbstractItem<ListModeBaseAdapterItem, ListModeBaseAdapterItem.ViewHolder> {
private static final ViewHolderFactory<? extends ViewHolder> FACTORY = new ItemFactory();
final ImageLoader imageLoader;
public Deal deal;
Context context;
public ListModeBaseAdapterItem(ImageLoader imageLoader) {
this.imageLoader = imageLoader;
}
public ListModeBaseAdapterItem withDeal(Deal deal) {
this.deal = deal;
return this;
}
@Override
public int getType() {
return R.id.list_mode_image_item_id;
}
@Override
public int getLayoutRes() {
return R.layout.item_list_mode_image;
}
@Override
public void bindView(ListModeBaseAdapterItem.ViewHolder viewHolder, List<Object> payloads) {
super.bindView(viewHolder, payloads);
context = viewHolder.itemView.getContext();
//View binding
}
@Override
public void unbindView(ViewHolder holder) {
super.unbindView(holder);
holder.dealImage.setImageDrawable(null);
holder.dealHeadline.setText(null);
holder.dealDiscount.setText(null);
holder.agreementIcon.setImageDrawable(null);
}
@Override
public ViewHolderFactory<? extends ViewHolder> getFactory() {
return FACTORY;
}
protected static class ItemFactory implements ViewHolderFactory<ViewHolder> {
public ViewHolder create(View v) {
return new ViewHolder(v);
}
}
protected static class ViewHolder extends RecyclerView.ViewHolder {
protected FrameLayout view;
@BindView(R.id.item_list_mode_image)
ImageView dealImage;
@BindView(R.id.item_list_mode_title)
TextView dealTitle;
@BindView(R.id.item_list_mode_headline)
TextView dealHeadline;
@BindView(R.id.item_list_mode_discount)
TextView dealDiscount;
@BindView(R.id.item_list_mode_agreement)
AppCompatImageView agreementIcon;
@BindView(R.id.item_list_mode_hot_badge)
TextView hotBadge;
@BindView(R.id.image_parent)
View imageParent;
public ViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
...
}
}
}
It works pretty fine in most cases. However, I have detected with Crashlytics that for some production users the app crashes with the following stacktrace. It seems the adapter expects a given item count, but receives less than that. However, it does not make too many sense, as I pass only a List of items to the adapter and I cannot see in which part of my code is the failure.
As an attempt to reproduce the bug, I tried to pass null
to itemAdapter.add
or a list with a null value, but that throws a different exception.
What could the the cause of this crash?
Fatal Exception: java.util.NoSuchElementException
at java.util.HashMap$HashIterator.nextEntry(HashMap.java:854)
at java.util.HashMap$ValueIterator.next(HashMap.java:879)
at co.xxx.xxx.ui.listmode.view.ListModeBaseAdapterItem.getLayoutRes(Unknown Source)
at co.xxx.xxx.ui.listmode.view.ListModeBaseAdapterItem.withDeal(Unknown Source)
at co.xxx.xxx.ui.listmode.view.ListModeBaseAdapterItem.withDeal(Unknown Source)
at com.mikepenz.fastadapter.FastAdapter$OnBindViewHolderListenerImpl.onBindViewHolder(Unknown Source)
at com.mikepenz.fastadapter.FastAdapter.withOnClickListener(Unknown Source)
at com.mikepenz.fastadapter.AbstractAdapter.wrap(Unknown Source)
at android.support.v7.widget.RecyclerView$Adapter.onCreateViewHolder(Unknown Source)
at android.support.v7.widget.RecyclerView$Recycler.clear(Unknown Source)
at android.support.v7.widget.RecyclerView$Recycler.clear(Unknown Source)
at android.support.v7.widget.RecyclerView$Recycler.clear(Unknown Source)
at android.support.v7.widget.RecyclerView$Recycler.getScrapList(Unknown Source)
at android.support.v7.widget.LinearLayoutManager$LayoutState.hasMore(Unknown Source)
at android.support.v7.widget.GridLayoutManager.setStackFromEnd(Unknown Source)
at android.support.v7.widget.LinearLayoutManager.generateDefaultLayoutParams(Unknown Source)
at android.support.v7.widget.LinearLayoutManager.onSaveInstanceState(Unknown Source)
at android.support.v7.widget.GridLayoutManager.onLayoutChildren(Unknown Source)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(Unknown Source)
at android.support.v7.widget.RecyclerView.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1705)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:797)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:657)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.support.design.widget.CoordinatorLayout.releaseTempRect(Unknown Source)
at android.support.design.widget.CoordinatorLayout.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.support.v4.view.ViewPager.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.support.design.widget.CoordinatorLayout.releaseTempRect(Unknown Source)
at android.support.design.widget.HeaderScrollingViewBehavior.onMeasureChild(Unknown Source)
at android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onRequestChildRectangleOnScreen(Unknown Source)
at android.support.design.widget.CoordinatorLayout.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.support.v4.widget.DrawerLayout.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:214)
at android.support.v7.widget.ContentFrameLayout.onMeasure(Unknown Source)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1705)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:797)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:657)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:214)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1705)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:797)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:657)
at android.view.View.measure(View.java:20084)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6282)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:214)
at com.android.internal.policy.DecorView.onMeasure(DecorView.java:714)
at android.view.View.measure(View.java:20084)
at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2627)
at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1577)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1846)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1462)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6960)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:907)
at android.view.Choreographer.doCallbacks(Choreographer.java:709)
at android.view.Choreographer.doFrame(Choreographer.java:644)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:893)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:208)
at android.app.ActivityThread.main(ActivityThread.java:6267)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)