GapWorker.java revision 1e0a453dff8d7bb7a966d006541454c770d1ed05
107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik/* 207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Copyright (C) 2016 The Android Open Source Project 307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * 407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Licensed under the Apache License, Version 2.0 (the "License"); 507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * you may not use this file except in compliance with the License. 607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * You may obtain a copy of the License at 707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * 807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * http://www.apache.org/licenses/LICENSE-2.0 907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * 1007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Unless required by applicable law or agreed to in writing, software 1107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * distributed under the License is distributed on an "AS IS" BASIS, 1207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * See the License for the specific language governing permissions and 1407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * limitations under the License. 1507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */ 1607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikpackage android.support.v7.widget; 1707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 1807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport android.support.v4.os.TraceCompat; 19dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craikimport android.view.View; 2007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 2107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.ArrayList; 2207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.Arrays; 23356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Collections; 24356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Comparator; 2507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.concurrent.TimeUnit; 2607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 271e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikfinal class GapWorker implements Runnable { 28dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>(); 3007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 31356880d3de117b067522ad452f4e3eed85ce444cChris Craik ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>(); 3207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik long mPostTimeNs; 3307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik long mFrameIntervalNs; 3407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 35356880d3de117b067522ad452f4e3eed85ce444cChris Craik static class Task { 36356880d3de117b067522ad452f4e3eed85ce444cChris Craik public boolean immediate; 37356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int viewVelocity; 38356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int distanceToItem; 39356880d3de117b067522ad452f4e3eed85ce444cChris Craik public RecyclerView view; 40356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int position; 41356880d3de117b067522ad452f4e3eed85ce444cChris Craik 42356880d3de117b067522ad452f4e3eed85ce444cChris Craik public void clear() { 43356880d3de117b067522ad452f4e3eed85ce444cChris Craik immediate = false; 44356880d3de117b067522ad452f4e3eed85ce444cChris Craik viewVelocity = 0; 45356880d3de117b067522ad452f4e3eed85ce444cChris Craik distanceToItem = 0; 46356880d3de117b067522ad452f4e3eed85ce444cChris Craik view = null; 47356880d3de117b067522ad452f4e3eed85ce444cChris Craik position = 0; 48356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 49356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 50356880d3de117b067522ad452f4e3eed85ce444cChris Craik 51356880d3de117b067522ad452f4e3eed85ce444cChris Craik /** 52356880d3de117b067522ad452f4e3eed85ce444cChris Craik * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects 53356880d3de117b067522ad452f4e3eed85ce444cChris Craik * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared 54356880d3de117b067522ad452f4e3eed85ce444cChris Craik * in between calls. 55356880d3de117b067522ad452f4e3eed85ce444cChris Craik */ 56356880d3de117b067522ad452f4e3eed85ce444cChris Craik private ArrayList<Task> mTasks = new ArrayList<>(); 57356880d3de117b067522ad452f4e3eed85ce444cChris Craik 58945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 591e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik * Prefetch information associated with a specific RecyclerView. 60945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 61945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik static class PrefetchRegistryImpl implements RecyclerView.PrefetchRegistry { 62945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik private int mPrefetchDx; 63945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik private int mPrefetchDy; 64945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int[] mPrefetchArray; 65945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 66945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int mCount; 67945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 68945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void setPrefetchVector(int dx, int dy) { 69945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchDx = dx; 70945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchDy = dy; 71945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 72945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) { 74945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mCount = 0; 75945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 76945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 77945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 78945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 79945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final RecyclerView.LayoutManager layout = view.mLayout; 80945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (view.mAdapter != null 81945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik && layout != null 821e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik && layout.isItemPrefetchEnabled()) { 831e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (nested) { 841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // nested prefetch, only if no adapter updates pending. Note: we don't query 851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // view.hasPendingAdapterUpdates(), as first layout may not have occurred 861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (!view.mAdapterHelper.hasPendingUpdates()) { 871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this); 881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // momentum based prefetch, only if we trust current child/adapter state 911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (!view.hasPendingAdapterUpdates()) { 921e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy, 931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.mState, this); 941e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 951e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 961e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 97945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mCount > layout.mPrefetchMaxCountObserved) { 98945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik layout.mPrefetchMaxCountObserved = mCount; 99945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik view.mRecycler.updateViewCacheSize(); 100945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 101945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 102945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 103945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 104945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 105945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public void addPosition(int layoutPosition, int pixelDistance) { 106945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (pixelDistance < 0) { 107945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik throw new IllegalArgumentException("Pixel distance must be non-negative"); 108945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 109945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 110945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // allocate or expand array as needed, doubling when needed 111945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int storagePosition = mCount * 2; 112945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray == null) { 113945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[4]; 114945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 115945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } else if (storagePosition >= mPrefetchArray.length) { 116945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int[] oldArray = mPrefetchArray; 117945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[storagePosition * 2]; 118945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length); 119945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 120945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 121945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // add position 122945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition] = layoutPosition; 123945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition + 1] = pixelDistance; 124945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 125945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mCount++; 126945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 127945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 128945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik boolean lastPrefetchIncludedPosition(int position) { 129945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 130945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int count = mCount * 2; 131945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik for (int i = 0; i < count; i += 2) { 132945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray[i] == position) return true; 133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 134945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 135945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return false; 136945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 137945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 138945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 139945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * Called when prefetch indices are no longer valid for cache prioritization. 140945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 141945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void clearPrefetchPositions() { 142945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 143945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 144945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 145945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 146945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 147945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 14807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void add(RecyclerView recyclerView) { 14907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) { 15007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView already present in worker list!"); 15107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 15207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerViews.add(recyclerView); 15307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 15407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 15507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void remove(RecyclerView recyclerView) { 15607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik boolean removeSuccess = mRecyclerViews.remove(recyclerView); 15707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !removeSuccess) { 15807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView removal failed!"); 15907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 16207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik /** 16307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Schedule a prefetch immediately after the current traversal. 16407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */ 16507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { 16607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (recyclerView.isAttachedToWindow()) { 16707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { 16807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("attempting to post unregistered view!"); 16907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mPostTimeNs == 0) { 171a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mPostTimeNs = recyclerView.getNanoTime(); 17207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik recyclerView.post(this); 17307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 176945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); 17707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 179356880d3de117b067522ad452f4e3eed85ce444cChris Craik static Comparator<Task> sTaskComparator = new Comparator<Task>() { 180356880d3de117b067522ad452f4e3eed85ce444cChris Craik @Override 181356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int compare(Task lhs, Task rhs) { 182356880d3de117b067522ad452f4e3eed85ce444cChris Craik // first, prioritize non-cleared tasks 183356880d3de117b067522ad452f4e3eed85ce444cChris Craik if ((lhs.view == null) != (rhs.view == null)) { 184356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.view == null ? 1 : -1; 185356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 186356880d3de117b067522ad452f4e3eed85ce444cChris Craik 187356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize immediate 188356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (lhs.immediate != rhs.immediate) { 189356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.immediate ? -1 : 1; 190356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 191356880d3de117b067522ad452f4e3eed85ce444cChris Craik 192356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _highest_ view velocity 193356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity; 194356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaViewVelocity != 0) return deltaViewVelocity; 195356880d3de117b067522ad452f4e3eed85ce444cChris Craik 196356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _lowest_ distance to item 197356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem; 198356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaDistanceToItem != 0) return deltaDistanceToItem; 199356880d3de117b067522ad452f4e3eed85ce444cChris Craik 200356880d3de117b067522ad452f4e3eed85ce444cChris Craik return 0; 201356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 202356880d3de117b067522ad452f4e3eed85ce444cChris Craik }; 203356880d3de117b067522ad452f4e3eed85ce444cChris Craik 204356880d3de117b067522ad452f4e3eed85ce444cChris Craik private void buildTaskList() { 205356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Update PrefetchRegistry in each view 206356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewCount = mRecyclerViews.size(); 207356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskCount = 0; 208356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 209356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 2101e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false); 211356880d3de117b067522ad452f4e3eed85ce444cChris Craik totalTaskCount += view.mPrefetchRegistry.mCount; 212356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 213356880d3de117b067522ad452f4e3eed85ce444cChris Craik 214356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Populate task list from prefetch data... 215356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.ensureCapacity(totalTaskCount); 216356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskIndex = 0; 217356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 218356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 219356880d3de117b067522ad452f4e3eed85ce444cChris Craik PrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry; 220356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx) 221356880d3de117b067522ad452f4e3eed85ce444cChris Craik + Math.abs(prefetchRegistry.mPrefetchDy); 222356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) { 223356880d3de117b067522ad452f4e3eed85ce444cChris Craik final Task task; 224356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (totalTaskIndex >= mTasks.size()) { 225356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = new Task(); 226356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.add(task); 227356880d3de117b067522ad452f4e3eed85ce444cChris Craik } else { 228356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = mTasks.get(totalTaskIndex); 229356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 230356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1]; 231356880d3de117b067522ad452f4e3eed85ce444cChris Craik 232356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.immediate = distanceToItem <= viewVelocity; 233356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.viewVelocity = viewVelocity; 234356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.distanceToItem = distanceToItem; 235356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.view = view; 236356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.position = prefetchRegistry.mPrefetchArray[j]; 237356880d3de117b067522ad452f4e3eed85ce444cChris Craik 238356880d3de117b067522ad452f4e3eed85ce444cChris Craik totalTaskIndex++; 239356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 240356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 24107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 242356880d3de117b067522ad452f4e3eed85ce444cChris Craik // ... and priority sort 243356880d3de117b067522ad452f4e3eed85ce444cChris Craik Collections.sort(mTasks, sTaskComparator); 244356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 245a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 246dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik static boolean isPrefetchPositionAttached(RecyclerView view, int position) { 247dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik final int childCount = view.mChildHelper.getUnfilteredChildCount(); 248dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik for (int i = 0; i < childCount; i++) { 249dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View attachedView = view.mChildHelper.getUnfilteredChildAt(i); 250dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView); 251dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // Note: can use mPosition here because adapter doesn't have pending updates 252dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik if (holder.mPosition == position && !holder.isInvalid()) { 253dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return true; 254dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 255dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 256dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return false; 257dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 258dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2591e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private RecyclerView.ViewHolder flushWorkWithDeadline(RecyclerView view, 2601e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik int position, long deadlineNs) { 2611e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (isPrefetchPositionAttached(view, position)) { 2621e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // don't attempt to prefetch attached views 2631e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return null; 2641e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 265a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 2661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.Recycler recycler = view.mRecycler; 2671e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline( 2681e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik position, false, deadlineNs); 2691e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 2701e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (holder != null) { 2711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (holder.isBound()) { 2721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Only give the view a chance to go into the cache if binding succeeded 2731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Note that we must use public method, since item may need cleanup 2741e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik recycler.recycleView(holder.itemView); 2751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 2761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Didn't bind, so we can't cache the view, but it will stay in the pool until 2771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // next prefetch/traversal. If a View fails to bind, it means we didn't have 2781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // enough time prior to the deadline (and won't for other instances of this 2791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // type, during this GapWorker prefetch pass). 2801e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik recycler.addViewHolderToRecycledViewPool(holder, false); 281dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 2821e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 2831e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return holder; 2841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 285dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private void flushTaskWithDeadline(Task task, long deadlineNs) { 2871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs; 2881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder holder = flushWorkWithDeadline(task.view, 2891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik task.position, taskDeadlineNs); 2901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (holder != null && holder.mNestedRecyclerView != null) { 2911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // do nested prefetch! 2921e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final RecyclerView innerView = holder.mNestedRecyclerView; 2931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final PrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry; 2941e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true); 2951e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 2961e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (innerPrefetchRegistry.mCount != 0) { 2971e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik try { 2981e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG); 2991e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik innerView.mState.prepareForNestedPrefetch(innerView.mAdapter); 3001e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) { 3011e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Note that we ignore immediate flag for inner items because 3021e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // we have lower confidence they're needed next frame. 3031e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i]; 3041e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik flushWorkWithDeadline(innerView, innerPosition, deadlineNs); 3051e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3061e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } finally { 3071e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik TraceCompat.endSection(); 308945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 309a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 3101e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3111e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3121e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 3131e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private void flushTasksWithDeadline(long deadlineNs) { 3141e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik for (int i = 0; i < mTasks.size(); i++) { 3151e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final Task task = mTasks.get(i); 3161e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (task.view == null) { 3171e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik break; // done with populated tasks 3181e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3191e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik flushTaskWithDeadline(task, deadlineNs); 320356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.clear(); 321a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 32207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 32307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 324356880d3de117b067522ad452f4e3eed85ce444cChris Craik void prefetch(long deadlineNs) { 325356880d3de117b067522ad452f4e3eed85ce444cChris Craik buildTaskList(); 326356880d3de117b067522ad452f4e3eed85ce444cChris Craik flushTasksWithDeadline(deadlineNs); 327356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 328356880d3de117b067522ad452f4e3eed85ce444cChris Craik 32907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik @Override 33007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void run() { 33107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik try { 33207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG); 33307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 33407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mRecyclerViews.isEmpty()) { 33507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // abort - no work to do 33607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 33707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 33807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 33907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // Query last vsync so we can predict next one. Note that drawing time not yet 34007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // valid in animation/input callbacks, so query it here to be safe. 341356880d3de117b067522ad452f4e3eed85ce444cChris Craik long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos( 34207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerViews.get(0).getDrawingTime()); 343356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (lastFrameVsyncNs == 0) { 34407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // abort - couldn't get last vsync for estimating next 34507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 34607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 34707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 348a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // TODO: consider rebasing deadline if frame was already dropped due to long UI work. 349a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Next frame will still wait for VSYNC, so we can still use the gap if it exists. 350356880d3de117b067522ad452f4e3eed85ce444cChris Craik long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs; 351356880d3de117b067522ad452f4e3eed85ce444cChris Craik 352356880d3de117b067522ad452f4e3eed85ce444cChris Craik prefetch(nextFrameNs); 35307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 35407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // TODO: consider rescheduling self, if there's more work to do 35507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } finally { 35607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mPostTimeNs = 0; 35707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.endSection(); 35807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 35907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 36007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik} 361