Endless RecyclerView with Asynctask
<p>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).
<br>
<strong>RecyclerView Adapter</strong></p>

<pre><code>public SearchResultAdapter(ArrayList&lt;String&gt; suggestions) {
    this.suggestions = suggestions;
}

public SearchResultAdapter(ArrayList&lt;Word&gt; 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 &gt; 0) //check for scroll down
            {
                visibleItemCount = mLayoutManager.getChildCount();
                totalItemCount = mLayoutManager.getItemCount();
                pastVisiblesItems = mLayoutManager.findFirstVisibleItemPosition();
                if (loading) {
                    if ((visibleItemCount + pastVisiblesItems + 14) &gt;= 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;
}
</code></pre>

<p><strong>OnLoadMore</strong></p>

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

<p><strong>LoadMoreTask</strong></p>

<pre><code> public LoadMoreTask(Request request, ArrayList&lt;Word&gt; 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 &amp;&amp; words.get(index)==null){
        words.remove(index);
        adapter.notifyItemRemoved(index+1);
    }
    adapter.notifyDataSetChanged();
    adapter.setLoading();
}
</code></pre>

<p>As it can be seen, when <code>onLoadMore</code> triggered, I connect to the webserver get more data and attach it to the previous <code>arraylist</code>. Adding null object in <code>preExecute</code> is done in order to change <code>viewHolder</code> type (I have little loading <code>progressBar</code> in the end of list, and it is deleted after the <code>asynctask</code> is finished).
This code works pretty well, except that <code>notifyDataSetChanged</code> 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? </p>