Issue
public class ProfileSearchRecyclerAdapter extends RecyclerView.Adapter<ProfileSearchRecyclerAdapter.ViewHolder>{
private static List<ProfileSearchResult> profileList = new ArrayList<>();
private List<ProfileSearchResult> profileListNew = new ArrayList<>();
private Context context;public ProfileSearchRecyclerAdapter(Context context){
this.context = context;
}
public void notifyChanges(List<ProfileSearchResult> changes){
profileListNew.addAll(changes);
AsyncTaskRunner taskRunner = new AsyncTaskRunner();
taskRunner.execute();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.search_result_profile_layout,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
RequestOptions profilePictureRequest = new RequestOptions();
profilePictureRequest.placeholder(R.drawable.index);
holder.name.setText(profileList.get(position).getName());
holder.tag.setText(profileList.get(position).getTagline());
Glide.with(context)
.applyDefaultRequestOptions(profilePictureRequest)
.load(profileList.get(position).getProfilePictureUrl())
.into(holder.thumbnail);
}
@Override
public int getItemCount() {
return profileList.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
View view;
public CircleImageView thumbnail;
public TextView name;
public TextView tag;
public ViewHolder(View itemView) {
super(itemView);
view = itemView;
name = view.findViewById(R.id.search_result_name);
tag = view.findViewById(R.id.search_result_tagline);
thumbnail = view.findViewById(R.id.search_result_thumbnail);
}
}
private class AsyncTaskRunner extends AsyncTask<List<ProfileSearchResult>, Void, Void> {
/**
* This method compares profileSearchResultChanges with profileList to add new files,
* remove deleted files and modify files (remove old version of a file and add it's new version)
* Asynchronously
* @param lists
* @return
*/
@Override
protected Void doInBackground(List<ProfileSearchResult>... lists) {
Iterator<ProfileSearchResult> iter = profileList.iterator();
while (iter.hasNext()) {
ProfileSearchResult result = iter.next();
if(false == profileListNew.contains(result)){
profileList.remove(result);
}
}
iter = profileListNew.iterator();
while (iter.hasNext()) {
ProfileSearchResult result = iter.next();
if(false == profileList.contains(result)){
profileList.add(result);
}
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
invalidate();
}
}
public void invalidate(){
notifyDataSetChanged();
}
}
This is the adapter class for my recyclerview. As Livedata registers an update for query snapshot, I pass the document to the adapter. When I assign
profileList = changes;
in notifyChanges() without any asynchronous operation, my recyclerview displays data. But when I do the Asynchronous operations, the RecyclerView is empty. What could be the reason? Thanks in advance
05-30 18:27:07.813 13453-14405/com.sachintitus.instafy.instafy E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #5
Process: com.sachintitus.instafy.instafy, PID: 13453
java.lang.RuntimeException: An error occurred while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:318)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:354)
at java.util.concurrent.FutureTask.setException(FutureTask.java:223)
at java.util.concurrent.FutureTask.run(FutureTask.java:242)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.sachintitus.instafy.instafy.adapters.ProfileSearchRecyclerAdapter$AsyncTaskRunner.doInBackground(ProfileSearchRecyclerAdapter.java:96)
at com.sachintitus.instafy.instafy.adapters.ProfileSearchRecyclerAdapter$AsyncTaskRunner.doInBackground(ProfileSearchRecyclerAdapter.java:82)
at android.os.AsyncTask$2.call(AsyncTask.java:304)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:243)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
at java.lang.Thread.run(Thread.java:761)
Solution
Do not call notifyDataSetChanged()
from any other thread than main thread. Just remove calls to invalidate()
from doInBackground()
and instead add it to onPostExecute()
@Override
protected void onPostExecute(Void aVoid) {
Log.w("PROCESS EXECUTED", String.valueOf(profileList.size()));
invalidate();
}
ArrayList should not be modified when iteration over it. Instead of using remove()
method on ArrayList, use remove()
method on Iterator.
Iterator<ProfileSearchResult> iter = profileList.iterator();
while (iter.hasNext()) {
ProfileSearchResult result = iter.next();
if(false == profileListNew.contains(result)){
iter.remove();
}
}
Also you may be running multiple AsyncTasks concurrently which may throw ConcurrentModificationException, so you should synchronize operations on lists by using lock so that only one thread can modify list at a time. Add this synchronized block to doInBackground()
in your AsyncTask like this
@Override
protected Void doInBackground(List<ProfileSearchResult>... lists) {
synchronized (profileList) {
Iterator<ProfileSearchResult> iter = profileList.iterator();
while (iter.hasNext()) {
ProfileSearchResult result = iter.next();
if(false == profileListNew.contains(result)){
iter.remove();
}
}
iter = profileListNew.iterator();
while (iter.hasNext()) {
ProfileSearchResult result = iter.next();
if(false == profileList.contains(result)){
profileList.add(result);
}
}
return null;
}
}
Answered By - Vladimír Bielený
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.