Skip to content

Instantly share code, notes, and snippets.

@fredgrott
Created September 6, 2016 18:32
Show Gist options
  • Save fredgrott/9ab424a4edf73177010fccd954d31562 to your computer and use it in GitHub Desktop.
Save fredgrott/9ab424a4edf73177010fccd954d31562 to your computer and use it in GitHub Desktop.

Original code developed by Robert LaThanh Modifications by Fred Grott, all under Apache License 2.0 Copyright (C) 2016

AdaptOnDemandSimpleBindingAdapter

public abstract class AdaptOnDemandSimpleBindingAdapter<A, VM,
        VH extends RecyclerView.ViewHolder &
                Taggable>
        extends SimpleBindingAdapter<A, VM, VH>  {

  private final @NonNull
  SimpleAdapter<VM, A> actualAdapter;


  private final @NonNull
  ExecutorService executorService;


  //== Constructors ===========================================================


  /**
   * Constructor for providing a custom executor service. For example, one that
   * uses fewer threads than the default, which means items would probably take
   * longer to adapt but may have less impact on the UI.
   */
  @SuppressWarnings("unused")
  public AdaptOnDemandSimpleBindingAdapter(
          @NonNull SimpleAdapter<VM, A> actualAdapter,
          @NonNull ExecutorService executorService) {
    this.actualAdapter = actualAdapter;
    this.executorService = executorService;
  }


  /**
   * The default executor service for adapting items uses a stack so the items
   * most recently scrolled into view are adapted first. Thus, items scrolled
   * into and then off of the screen before getting picked up for adapting are
   * adapted later. The number of threads it uses is up to double the number of
   * CPUs ({@code numCpus * 2 + 1}).
   */
  public AdaptOnDemandSimpleBindingAdapter(
          @NonNull SimpleAdapter<VM, A> actualAdapter) {
    this.actualAdapter = actualAdapter;

    // queue size must be at least as large as the number of items possibly on
    // the screen
    BlockingQueue<Runnable> queue = new LinkedBlockingDeque<Runnable>(128) {
      @Override
      public Runnable poll() {
        return super.pollLast();
      }
    };

    int cpus = Runtime.getRuntime().availableProcessors();
    this.executorService = new ThreadPoolExecutor(cpus + 1, cpus * 2 + 1,
            1, TimeUnit.SECONDS, queue);


  }

  //== 'RecyclerView.Adapter' methods =========================================

  @Override
  public void onBindViewHolder(final VH loadingViewHolder,
                               int position) {
    final AdaptableViewModel<A, VM> adaptableViewModel = items.get(position);
    VM viewModel = adaptableViewModel.viewModel;
    loadingViewHolder.setTag(adaptableViewModel);

    if (viewModel == null) {
      // item not yet adapted. start task to make this view available
      new AsyncTask<Void, Void, VM>() {
        @Override
        protected VM doInBackground(Void... params) {
          if (adaptableViewModel.viewModel != null) {
            // another task must have beat me to adapting. (the item was
            // scrolled onto screen, I was created, the item was scrolled off
            // and back on, another duplicate task was added to the top of the
            // queue, that task finished, then I got executed.)
            return adaptableViewModel.viewModel;
          }
          // this could still be a duplicate, concurrent job. oh well.
          return actualAdapter.adapt(adaptableViewModel.adaptable);
        }

        @Override
        protected void onPostExecute(VM viewModel) {
          // save the adapted model to the AdaptableViewModel so it's available
          // if it's requested again
          adaptableViewModel.setViewModel(viewModel);
          if (loadingViewHolder.getTag() == adaptableViewModel) {
            onViewModelReadyForViewHolder(loadingViewHolder, viewModel);
          }
        }
      }.executeOnExecutor(executorService);
    }

    // let implementation now do actual binding.
    assert viewModel != null;
    onBindViewHolder(loadingViewHolder, viewModel, position);
  } // onBindViewHolder()




}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment