1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.recyclerview.widget; 18 19 import android.os.Handler; 20 import android.os.Looper; 21 22 import androidx.annotation.NonNull; 23 import androidx.annotation.RestrictTo; 24 25 import java.util.concurrent.Executor; 26 import java.util.concurrent.Executors; 27 28 /** 29 * Configuration object for {@link ListAdapter}, {@link AsyncListDiffer}, and similar 30 * background-thread list diffing adapter logic. 31 * <p> 32 * At minimum, defines item diffing behavior with a {@link DiffUtil.ItemCallback}, used to compute 33 * item differences to pass to a RecyclerView adapter. 34 * 35 * @param <T> Type of items in the lists, and being compared. 36 */ 37 public final class AsyncDifferConfig<T> { 38 @NonNull 39 private final Executor mMainThreadExecutor; 40 @NonNull 41 private final Executor mBackgroundThreadExecutor; 42 @NonNull 43 private final DiffUtil.ItemCallback<T> mDiffCallback; 44 45 private AsyncDifferConfig( 46 @NonNull Executor mainThreadExecutor, 47 @NonNull Executor backgroundThreadExecutor, 48 @NonNull DiffUtil.ItemCallback<T> diffCallback) { 49 mMainThreadExecutor = mainThreadExecutor; 50 mBackgroundThreadExecutor = backgroundThreadExecutor; 51 mDiffCallback = diffCallback; 52 } 53 54 /** @hide */ 55 @SuppressWarnings("WeakerAccess") 56 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 57 @NonNull 58 public Executor getMainThreadExecutor() { 59 return mMainThreadExecutor; 60 } 61 62 @SuppressWarnings("WeakerAccess") 63 @NonNull 64 public Executor getBackgroundThreadExecutor() { 65 return mBackgroundThreadExecutor; 66 } 67 68 @SuppressWarnings("WeakerAccess") 69 @NonNull 70 public DiffUtil.ItemCallback<T> getDiffCallback() { 71 return mDiffCallback; 72 } 73 74 /** 75 * Builder class for {@link AsyncDifferConfig}. 76 * 77 * @param <T> 78 */ 79 public static final class Builder<T> { 80 private Executor mMainThreadExecutor; 81 private Executor mBackgroundThreadExecutor; 82 private final DiffUtil.ItemCallback<T> mDiffCallback; 83 84 public Builder(@NonNull DiffUtil.ItemCallback<T> diffCallback) { 85 mDiffCallback = diffCallback; 86 } 87 88 /** 89 * If provided, defines the main thread executor used to dispatch adapter update 90 * notifications on the main thread. 91 * <p> 92 * If not provided, it will default to the main thread. 93 * 94 * @param executor The executor which can run tasks in the UI thread. 95 * @return this 96 * 97 * @hide 98 */ 99 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 100 @NonNull 101 public Builder<T> setMainThreadExecutor(Executor executor) { 102 mMainThreadExecutor = executor; 103 return this; 104 } 105 106 /** 107 * If provided, defines the background executor used to calculate the diff between an old 108 * and a new list. 109 * <p> 110 * If not provided, defaults to two thread pool executor, shared by all ListAdapterConfigs. 111 * 112 * @param executor The background executor to run list diffing. 113 * @return this 114 */ 115 @SuppressWarnings({"unused", "WeakerAccess"}) 116 @NonNull 117 public Builder<T> setBackgroundThreadExecutor(Executor executor) { 118 mBackgroundThreadExecutor = executor; 119 return this; 120 } 121 122 private static class MainThreadExecutor implements Executor { 123 final Handler mHandler = new Handler(Looper.getMainLooper()); 124 @Override 125 public void execute(@NonNull Runnable command) { 126 mHandler.post(command); 127 } 128 } 129 130 /** 131 * Creates a {@link AsyncListDiffer} with the given parameters. 132 * 133 * @return A new AsyncDifferConfig. 134 */ 135 @NonNull 136 public AsyncDifferConfig<T> build() { 137 if (mMainThreadExecutor == null) { 138 mMainThreadExecutor = sMainThreadExecutor; 139 } 140 if (mBackgroundThreadExecutor == null) { 141 synchronized (sExecutorLock) { 142 if (sDiffExecutor == null) { 143 sDiffExecutor = Executors.newFixedThreadPool(2); 144 } 145 } 146 mBackgroundThreadExecutor = sDiffExecutor; 147 } 148 return new AsyncDifferConfig<>( 149 mMainThreadExecutor, 150 mBackgroundThreadExecutor, 151 mDiffCallback); 152 } 153 154 // TODO: remove the below once supportlib has its own appropriate executors 155 private static final Object sExecutorLock = new Object(); 156 private static Executor sDiffExecutor = null; 157 158 // TODO: use MainThreadExecutor from supportlib once one exists 159 private static final Executor sMainThreadExecutor = new MainThreadExecutor(); 160 } 161 } 162