RecyclerView не отображает данные при выполнении асинхронных операций - PullRequest
0 голосов
/ 31 мая 2018
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();
}

}

Это класс адаптера для моего переработчика.Поскольку Livedata регистрирует обновление для моментального снимка запроса, я передаю документ адаптеру.Когда я назначаю

profileList = changes;

в notifyChanges () без какой-либо асинхронной операции, в моем окне повторного просмотра отображаются данные.Но когда я делаю асинхронные операции, RecyclerView становится пустым.Что может быть причиной?Заранее спасибо

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) 

Ответы [ 2 ]

0 голосов
/ 31 мая 2018

Я считаю, что проблема ConcurrentModificationException здесь

Iterator<ProfileSearchResult> iter = profileList.iterator();
        while (iter.hasNext()) {
            ProfileSearchResult result = iter.next();

            if(false == profileListNew.contains(result)){
                profileList.remove(result); // <------- here <-------
            }
        }

В Java, если вы хотите удалить элемент из списка при использовании его итератора, вы должны использовать метод итератора, чтобы сделать это или столкнутьсяConcurrentModificationException

Это в основном защитный механизм, чтобы убедиться, что итератор не будет дважды передавать один и тот же список или пропускать узел. ссылка на документацию

вместо profileList.remove(result);

try iter.remove()

проверьте это пример

Надеюсь, это поможет

0 голосов
/ 31 мая 2018

Не вызывайте notifyDataSetChanged() из любого другого потока, кроме основного потока.Просто удалите вызовы invalidate() из doInBackground() и вместо этого добавьте его в onPostExecute()

@Override
protected void onPostExecute(Void aVoid) {
    Log.w("PROCESS EXECUTED", String.valueOf(profileList.size()));
    invalidate();
}

ArrayList не должен изменяться при итерации по нему.Вместо использования метода remove() в ArrayList, используйте метод remove() в Итераторе.

Iterator<ProfileSearchResult> iter = profileList.iterator();
while (iter.hasNext()) {
    ProfileSearchResult result = iter.next();

    if(false == profileListNew.contains(result)){
        iter.remove();
    }
}

Также вы можете запускать несколько AsyncTasks одновременно, что может вызвать исключение ConcurrentModificationException, поэтому вы должны синхронизировать операции со списками с помощью блокировкитак что только один поток может изменять список одновременно.Добавьте этот синхронизированный блок к doInBackground() в AsyncTask, как это

@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;
    }
}
...