Android: how to add items and refresh list when using PagedList?

南楼画角 提交于 2019-12-10 18:56:11

问题


I've used PagedList for loading page of items and display them in RecyclerView, and it is successfully displaying list of numbers(0,1,2,3....,99), the list can always be scroll up and down without any problem. When I tried to click a button to call appendItems() to append new data, its still fine. However, the app crashed when I scroll the list.

java.util.ConcurrentModificationException
    at java.util.AbstractList$SubAbstractList.size(AbstractList.java:360)
    at androidx.paging.PagedStorage.get(PagedStorage.java:153)
    at androidx.paging.PagedList.get(PagedList.java:384)
    at androidx.paging.AsyncPagedListDiffer.getItem(AsyncPagedListDiffer.java:206)
    at androidx.paging.PagedListAdapter.getItem(PagedListAdapter.java:156)
    at com.mysample.pagedlist.MyPagedListAdapter.onBindViewHolder(MyPagedListAdapter.java:38)
    at com.mysample.pagedlist.MyPagedListAdapter.onBindViewHolder(MyPagedListAdapter.java:19)

It seems I don't know the right way to append more data and refresh the display. Can anyone tell me how to work it out?

Api is served as database and dao

public class Api {
    private static List<String> list = null;

    private static void initListIfNeeded() {
        if (list == null) {
            list = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                list.add("" + i);
            }
        }
    }

    public static List<String> pageItems(int page, int pageSize) {
        initListIfNeeded();
        int start = page * pageSize;
        int end = (page + 1) * pageSize;
        if (end > list.size()) {
            end = list.size();
        }
        if (page < 0 || start >= list.size()) {
            return new ArrayList<>();
        } else {
            return list.subList(start, end);
        }
    }

    public static void appendItems() {
        initListIfNeeded();
        int size = list.size();
        for (int i = 0; i < 10; i++) {
            list.add("" + (size + i));
        }
    }
}

DataSource used for handling pagination of data

public class MyDataSource extends PageKeyedDataSource<Integer, String> {

    public MyDataSource() {
    }

    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, String> callback) {
        callback.onResult(Api.pageItems(0, params.requestedLoadSize), -1, 1);
    }

    @Override
    public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, String> callback) {
        callback.onResult(Api.pageItems(params.key, params.requestedLoadSize), params.key - 1);
    }

    @Override
    public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, String> callback) {
        callback.onResult(Api.pageItems(params.key, params.requestedLoadSize), params.key + 1);
    }

}

The corresponding ViewModel class

public class MyViewModel extends ViewModel {

    public LiveData<PagedList<String>> list;

    public MyViewModel() {
        list = new LivePagedListBuilder<>(new DataSource.Factory<Integer, String>() {
            @Override
            public DataSource<Integer, String> create() {
                return new MyDataSource();
            }
        },  new PagedList.Config.Builder().setPageSize(20).setInitialLoadSizeHint(20).build()).build();
    }

    public void appendItems() {
        Api.appendItems();
    }
}

The activity

public class PagedListActivity extends AppCompatActivity {

    private MyPagedListAdapter adapter;
    private MyViewModel viewModel;
    private RecyclerView recyclerView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_paged_list);
        adapter = new MyPagedListAdapter(LayoutInflater.from(this));

        recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerView.setAdapter(adapter);
        viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
        viewModel.list.observe(this, new Observer<PagedList<String>>() {
            @DebugLog
            @Override
            public void onChanged(PagedList<String> strings) {
                adapter.submitList(strings);
            }
        });
    }

    @DebugLog
    public void onClick(View view) {
        viewModel.appendItems();
    }
}

The adapter

public class MyPagedListAdapter extends PagedListAdapter<String, MyPagedListAdapter.VH> {

    private LayoutInflater inflater;

    protected MyPagedListAdapter(LayoutInflater inflater) {
        super(DIFF_CALLBACK);
        this.inflater = inflater;
    }

    @NonNull
    @Override
    public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = inflater.inflate(R.layout.item_text, parent, false);
        return new VH(view);
    }

    @Override
    public void onBindViewHolder(@NonNull VH holder, int position) {
        holder.setData(getItem(position));
    }

    public class VH extends RecyclerView.ViewHolder {

        private final TextView txt;

        public VH(@NonNull View itemView) {
            super(itemView);
            txt = itemView.findViewById(R.id.txt);
        }

        public void setData(String text) {
            txt.setText(text);
        }
    }

    private static DiffUtil.ItemCallback<String> DIFF_CALLBACK = new DiffUtil.ItemCallback<String>() {
        @Override
        public boolean areItemsTheSame(@NonNull String oldItem, @NonNull String newItem) {
            return oldItem.equalsIgnoreCase(newItem);
        }

        @Override
        public boolean areContentsTheSame(@NonNull String oldItem, @NonNull String newItem) {
            return oldItem.equalsIgnoreCase(newItem);
        }
    };

}

回答1:


From this stacktrace:

java.util.ConcurrentModificationException
    at java.util.AbstractList$SubAbstractList.size(AbstractList.java:360)

we can tell that

a) app crashes when someone is trying to modify list while iteration over list is in process
b) this happens on SubAbstractList instance (that is created when you call list.subList(start, end);

App crashes because sublist created before original list modifications is no longer valid, after you modify (append items to) originial list

Instead of returning list.subList(start, end); from Api#pageItems try
returning new ArrayList<>(list.subList(start, end));

For RecyclerView to display your changes, you should call Adapter#notifyItemRangeInserted or RW adapter



来源:https://stackoverflow.com/questions/52891690/android-how-to-add-items-and-refresh-list-when-using-pagedlist

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!