Search code examples
androidandroid-asynctaskandroid-recyclerviewnotifydatasetchanged

Endless RecyclerView with Asynctask


I have an app which has endless recyclerView (when certain offset to the end of list is reached, I call interface method from recyclerView adapter).
RecyclerView Adapter

public SearchResultAdapter(ArrayList<String> suggestions) {
    this.suggestions = suggestions;
}

public SearchResultAdapter(ArrayList<Word> results, RecyclerView recyclerView) {
    words = results;
    this.recyclerView = recyclerView;
    final LinearLayoutManager mLayoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            if (dy > 0) //check for scroll down
            {
                visibleItemCount = mLayoutManager.getChildCount();
                totalItemCount = mLayoutManager.getItemCount();
                pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
                if (loading) {
                    if ((visibleItemCount + pastVisiblesItems + 14) >= totalItemCount) {
                        if (mOnLoadMoreListener != null)
                            mOnLoadMoreListener.onLoadMore();
                        loading = false;
                    }
                }
            }
        }
    });
}

public void setOnLoadMoreListener(OnLoadMoreListener mOnLoadMoreListener) {
    this.mOnLoadMoreListener = mOnLoadMoreListener;
}

public void setOnSearchResultClickListener(OnSearchResultClickListener mClickListener) {
    this.mClickListener = mClickListener;
}

public void setLoading() {
    loading = true;
}

@Override
public int getItemViewType(int position) {
   if(words !=null)
       return words.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
    else 
       return VIEW_TYPE_SUGGESTION;
}

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    Context context = parent.getContext();
    LayoutInflater inflater = LayoutInflater.from(context);
    if (suggestions != null) {
        View view = inflater.inflate(R.layout.activity_suggestion_item, parent, false);
        return new SuggestionsViewHolder(view);
    }
    if (words != null) {
        // Inflate the custom layout
        if (viewType == VIEW_TYPE_ITEM) {
            View view = inflater.inflate(R.layout.activity_main_search_result_item, parent, false);
            return new WordViewHolder(view);
        }
        //inflate loading
        else if (viewType == VIEW_TYPE_LOADING) {
            View view = inflater.inflate(R.layout.activity_main_search_result_loading, parent, false);
            return new LoadingViewHolder(view);
        }
    }
    return null;
}

@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
    Word word=null;
    if(words !=null)
    word = words.get(position);
    if (viewHolder instanceof LoadingViewHolder) {
        LoadingViewHolder holder = (LoadingViewHolder) viewHolder;
        holder.progressBar.setIndeterminate(true);
    } else if (viewHolder instanceof WordViewHolder) {
        WordViewHolder holder = (WordViewHolder) viewHolder;
        holder.wordTextView.setText(word.getTitle());
        holder.sourceTextView.setText(word.getSource().getTitle());
    } else if (viewHolder instanceof SuggestionsViewHolder){
        SuggestionsViewHolder holder = (SuggestionsViewHolder) viewHolder;
        holder.wordTextView.setText(suggestions.get(position));
    }
    //wordTextView.loadDataWithBaseURL(null,result.toString(), "text/html", "utf-8", null);
}

@Override
public int getItemCount() {
    return words == null ? (suggestions ==null?0:suggestions.size()) : words.size();
}

public static class LoadingViewHolder extends RecyclerView.ViewHolder {
    public ProgressBar progressBar;
    public LoadingViewHolder(View itemView) {
        super(itemView);
        progressBar = (ProgressBar) itemView.findViewById(R.id.search_item_progressbar);
    }
}

public class WordViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    // public TextView titleTextView;
    private CustomTextView wordTextView;
    private CustomTextView sourceTextView;
    private CardView searchResultItem;
    public WordViewHolder(View itemView) {
        super(itemView);
        searchResultItem = (CardView) itemView.findViewById(R.id.search_result_item_cardview);
        wordTextView = (CustomTextView) itemView.findViewById(R.id.search_result_item_body);
        sourceTextView = (CustomTextView) itemView.findViewById(R.id.search_result_source);
        searchResultItem.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
       mClickListener.onSearchResultClick(getAdapterPosition());
    }
}

public class SuggestionsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    // public TextView titleTextView;
    private CustomTextView wordTextView;
    private CardView suggestionItem;
    public SuggestionsViewHolder(View itemView) {
        super(itemView);
        suggestionItem = (CardView) itemView.findViewById(R.id.suggestion_item_cardview);
        wordTextView = (CustomTextView) itemView.findViewById(R.id.activity_suggestion_title);
        wordTextView.setTypeface(MainActivity.custom_font);
        suggestionItem.setOnClickListener(this);
    }

    @Override
    public void onClick(View view) {
            mClickListener.onSuggestionClick(suggestions.get(getAdapterPosition()));
    }
}
@Override
public int getItemViewType(int position) {
   if(words !=null)
       return words.get(position) == null ? VIEW_TYPE_LOADING : VIEW_TYPE_ITEM;
    else 
       return VIEW_TYPE_SUGGESTION;
}

OnLoadMore

 @Override
public void onLoadMore() {
    if (request.getPage() < totalPageAmount) {
        request.nextPage();
        LoadMoreTask task = new LoadMoreTask(request, words, adapter);
        task.execute();
    }
}

LoadMoreTask

 public LoadMoreTask(Request request, ArrayList<Word> words, SearchResultAdapter adapter){
    this.request=request;
    this.words=words;
    this.adapter=adapter;
    this.connection = new Connection(request.getUrl());
}

@Override
protected void onPreExecute() {
    super.onPreExecute();
    if(words.get(words.size()-1)!=null){
        words.add(null);
        index = words.size() - 1;
        adapter.notifyItemInserted(index);
    }
}

@Override
protected Void doInBackground(Void... params) {
    connection.connect();
    String result = connection.getStringFromServer();
    Respond respond = new Respond(result);
    if(!request.isDetailed())
        words.addAll(respond.handleSimple());
    return null;
}

@Override
protected void onPostExecute(Void aVoid) {
    super.onPostExecute(aVoid);
    connection.disconnect();
    if(index!=0 && words.get(index)==null){
        words.remove(index);
        adapter.notifyItemRemoved(index+1);
    }
    adapter.notifyDataSetChanged();
    adapter.setLoading();
}

As it can be seen, when onLoadMore triggered, I connect to the webserver get more data and attach it to the previous arraylist. Adding null object in preExecute is done in order to change viewHolder type (I have little loading progressBar in the end of list, and it is deleted after the asynctask is finished). This code works pretty well, except that notifyDataSetChanged freezes UI a little bit. Yes it is small enough and seen only if you scroll too fast, but anyway it is annoying. How I can optimize this code to remove freezes?


Solution

  • I solved the problem using notifyItemRangeInserted function, it seems that this function needs less resources than notifyDatasetChanged

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        connection.disconnect();
        adapter.resetLoading();
        if(index!=0 && words.get(index)==null){
            words.remove(index);
            adapter.notifyItemRemoved(index);
        }
        adapter.notifyItemRangeInserted(index,30);
    }
    

    So using this code everything works perfectly without any even nano freezes.