GapWorker.java revision 8123166eaf758eacc6ba5854c9b29d99f21388d6
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 188123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craikimport android.support.annotation.Nullable; 1907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport android.support.v4.os.TraceCompat; 20dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craikimport android.view.View; 2107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 2207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.ArrayList; 2307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.Arrays; 24356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Collections; 25356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Comparator; 2607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.concurrent.TimeUnit; 2707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 281e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craikfinal class GapWorker implements Runnable { 29dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 3007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>(); 3107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 32356880d3de117b067522ad452f4e3eed85ce444cChris Craik ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>(); 3307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik long mPostTimeNs; 3407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik long mFrameIntervalNs; 3507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 36356880d3de117b067522ad452f4e3eed85ce444cChris Craik static class Task { 37356880d3de117b067522ad452f4e3eed85ce444cChris Craik public boolean immediate; 38356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int viewVelocity; 39356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int distanceToItem; 40356880d3de117b067522ad452f4e3eed85ce444cChris Craik public RecyclerView view; 41356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int position; 42356880d3de117b067522ad452f4e3eed85ce444cChris Craik 43356880d3de117b067522ad452f4e3eed85ce444cChris Craik public void clear() { 44356880d3de117b067522ad452f4e3eed85ce444cChris Craik immediate = false; 45356880d3de117b067522ad452f4e3eed85ce444cChris Craik viewVelocity = 0; 46356880d3de117b067522ad452f4e3eed85ce444cChris Craik distanceToItem = 0; 47356880d3de117b067522ad452f4e3eed85ce444cChris Craik view = null; 48356880d3de117b067522ad452f4e3eed85ce444cChris Craik position = 0; 49356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 50356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 51356880d3de117b067522ad452f4e3eed85ce444cChris Craik 52356880d3de117b067522ad452f4e3eed85ce444cChris Craik /** 53356880d3de117b067522ad452f4e3eed85ce444cChris Craik * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects 54356880d3de117b067522ad452f4e3eed85ce444cChris Craik * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared 55356880d3de117b067522ad452f4e3eed85ce444cChris Craik * in between calls. 56356880d3de117b067522ad452f4e3eed85ce444cChris Craik */ 57356880d3de117b067522ad452f4e3eed85ce444cChris Craik private ArrayList<Task> mTasks = new ArrayList<>(); 58356880d3de117b067522ad452f4e3eed85ce444cChris Craik 59945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 601e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik * Prefetch information associated with a specific RecyclerView. 61945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 623104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik static class LayoutPrefetchRegistryImpl 633104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik implements RecyclerView.LayoutManager.LayoutPrefetchRegistry { 640951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik int mPrefetchDx; 650951d1a9c3200368a7b19bd272f7d6c266a11869Chris Craik int mPrefetchDy; 66945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int[] mPrefetchArray; 67945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 68945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik int mCount; 69945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 70945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void setPrefetchVector(int dx, int dy) { 71945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchDx = dx; 72945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchDy = dy; 73945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 74945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) { 76945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mCount = 0; 77945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 78945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 79945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 80945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 81945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final RecyclerView.LayoutManager layout = view.mLayout; 82945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (view.mAdapter != null 83945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik && layout != null 841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik && layout.isItemPrefetchEnabled()) { 851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (nested) { 861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // nested prefetch, only if no adapter updates pending. Note: we don't query 871e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // view.hasPendingAdapterUpdates(), as first layout may not have occurred 881e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (!view.mAdapterHelper.hasPendingUpdates()) { 891e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this); 901e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 911e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 921e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // momentum based prefetch, only if we trust current child/adapter state 931e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (!view.hasPendingAdapterUpdates()) { 941e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy, 951e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.mState, this); 961e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 971e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 981e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 99945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mCount > layout.mPrefetchMaxCountObserved) { 100945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik layout.mPrefetchMaxCountObserved = mCount; 101945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik view.mRecycler.updateViewCacheSize(); 102945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 103945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 104945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 105945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 106945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 107945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public void addPosition(int layoutPosition, int pixelDistance) { 108945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (pixelDistance < 0) { 109945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik throw new IllegalArgumentException("Pixel distance must be non-negative"); 110945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 111945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 112945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // allocate or expand array as needed, doubling when needed 113945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int storagePosition = mCount * 2; 114945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray == null) { 115945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[4]; 116945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 117945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } else if (storagePosition >= mPrefetchArray.length) { 118945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int[] oldArray = mPrefetchArray; 119945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[storagePosition * 2]; 120945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length); 121945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 122945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 123945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // add position 124945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition] = layoutPosition; 125945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition + 1] = pixelDistance; 126945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 127945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mCount++; 128945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 129945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 130945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik boolean lastPrefetchIncludedPosition(int position) { 131945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 132945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int count = mCount * 2; 133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik for (int i = 0; i < count; i += 2) { 134945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray[i] == position) return true; 135945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 136945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 137945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return false; 138945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 139945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 140945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 141945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * Called when prefetch indices are no longer valid for cache prioritization. 142945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 143945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void clearPrefetchPositions() { 144945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 145945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 146945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 147945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 148945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 149945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 15007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void add(RecyclerView recyclerView) { 15107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) { 15207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView already present in worker list!"); 15307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 15407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerViews.add(recyclerView); 15507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 15607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 15707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void remove(RecyclerView recyclerView) { 15807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik boolean removeSuccess = mRecyclerViews.remove(recyclerView); 15907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !removeSuccess) { 16007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView removal failed!"); 16107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 16407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik /** 16507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Schedule a prefetch immediately after the current traversal. 16607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */ 16707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { 16807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (recyclerView.isAttachedToWindow()) { 16907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { 17007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("attempting to post unregistered view!"); 17107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mPostTimeNs == 0) { 173a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mPostTimeNs = recyclerView.getNanoTime(); 17407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik recyclerView.post(this); 17507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 178945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); 17907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 18007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 181356880d3de117b067522ad452f4e3eed85ce444cChris Craik static Comparator<Task> sTaskComparator = new Comparator<Task>() { 182356880d3de117b067522ad452f4e3eed85ce444cChris Craik @Override 183356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int compare(Task lhs, Task rhs) { 184356880d3de117b067522ad452f4e3eed85ce444cChris Craik // first, prioritize non-cleared tasks 185356880d3de117b067522ad452f4e3eed85ce444cChris Craik if ((lhs.view == null) != (rhs.view == null)) { 186356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.view == null ? 1 : -1; 187356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 188356880d3de117b067522ad452f4e3eed85ce444cChris Craik 189356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize immediate 190356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (lhs.immediate != rhs.immediate) { 191356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.immediate ? -1 : 1; 192356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 193356880d3de117b067522ad452f4e3eed85ce444cChris Craik 194356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _highest_ view velocity 195356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity; 196356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaViewVelocity != 0) return deltaViewVelocity; 197356880d3de117b067522ad452f4e3eed85ce444cChris Craik 198356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _lowest_ distance to item 199356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem; 200356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaDistanceToItem != 0) return deltaDistanceToItem; 201356880d3de117b067522ad452f4e3eed85ce444cChris Craik 202356880d3de117b067522ad452f4e3eed85ce444cChris Craik return 0; 203356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 204356880d3de117b067522ad452f4e3eed85ce444cChris Craik }; 205356880d3de117b067522ad452f4e3eed85ce444cChris Craik 206356880d3de117b067522ad452f4e3eed85ce444cChris Craik private void buildTaskList() { 207356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Update PrefetchRegistry in each view 208356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewCount = mRecyclerViews.size(); 209356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskCount = 0; 210356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 211356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 2121e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false); 213356880d3de117b067522ad452f4e3eed85ce444cChris Craik totalTaskCount += view.mPrefetchRegistry.mCount; 214356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 215356880d3de117b067522ad452f4e3eed85ce444cChris Craik 216356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Populate task list from prefetch data... 217356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.ensureCapacity(totalTaskCount); 218356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskIndex = 0; 219356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 220356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 2213104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry; 222356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx) 223356880d3de117b067522ad452f4e3eed85ce444cChris Craik + Math.abs(prefetchRegistry.mPrefetchDy); 224356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) { 225356880d3de117b067522ad452f4e3eed85ce444cChris Craik final Task task; 226356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (totalTaskIndex >= mTasks.size()) { 227356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = new Task(); 228356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.add(task); 229356880d3de117b067522ad452f4e3eed85ce444cChris Craik } else { 230356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = mTasks.get(totalTaskIndex); 231356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 232356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1]; 233356880d3de117b067522ad452f4e3eed85ce444cChris Craik 234356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.immediate = distanceToItem <= viewVelocity; 235356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.viewVelocity = viewVelocity; 236356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.distanceToItem = distanceToItem; 237356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.view = view; 238356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.position = prefetchRegistry.mPrefetchArray[j]; 239356880d3de117b067522ad452f4e3eed85ce444cChris Craik 240356880d3de117b067522ad452f4e3eed85ce444cChris Craik totalTaskIndex++; 241356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 242356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 24307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 244356880d3de117b067522ad452f4e3eed85ce444cChris Craik // ... and priority sort 245356880d3de117b067522ad452f4e3eed85ce444cChris Craik Collections.sort(mTasks, sTaskComparator); 246356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 247a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 248dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik static boolean isPrefetchPositionAttached(RecyclerView view, int position) { 249dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik final int childCount = view.mChildHelper.getUnfilteredChildCount(); 250dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik for (int i = 0; i < childCount; i++) { 251dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View attachedView = view.mChildHelper.getUnfilteredChildAt(i); 252dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView); 253dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // Note: can use mPosition here because adapter doesn't have pending updates 254dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik if (holder.mPosition == position && !holder.isInvalid()) { 255dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return true; 256dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 257dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 258dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return false; 259dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 260dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2618123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view, 2621e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik int position, long deadlineNs) { 2631e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (isPrefetchPositionAttached(view, position)) { 2641e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // don't attempt to prefetch attached views 2651e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return null; 2661e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 267a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 2681e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.Recycler recycler = view.mRecycler; 2691e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline( 2701e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik position, false, deadlineNs); 2711e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 2721e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (holder != null) { 2731e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (holder.isBound()) { 2741e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Only give the view a chance to go into the cache if binding succeeded 2751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Note that we must use public method, since item may need cleanup 2761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik recycler.recycleView(holder.itemView); 2771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } else { 2781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // Didn't bind, so we can't cache the view, but it will stay in the pool until 2791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // next prefetch/traversal. If a View fails to bind, it means we didn't have 2801e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // enough time prior to the deadline (and won't for other instances of this 2811e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // type, during this GapWorker prefetch pass). 2821e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik recycler.addViewHolderToRecycledViewPool(holder, false); 283dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 2841e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 2851e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return holder; 2861e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 287dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2888123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView, 2898123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik long deadlineNs) { 2908123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerView == null) { 2918123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik return; 2928123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 2930fab45107f043a4856aad2e1fd785b396898616eChris Craik 2948123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerView.mDataSetHasChangedAfterLayout 2958123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik && innerView.mChildHelper.getUnfilteredChildCount() != 0) { 2968123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // RecyclerView has new data, but old attached views. Clear everything, so that 2978123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // we can prefetch without partially stale data. 2988123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerView.removeAndRecycleViews(); 2998123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3000fab45107f043a4856aad2e1fd785b396898616eChris Craik 3018123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // do nested prefetch! 3028123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry; 3038123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true); 3048123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 3058123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerPrefetchRegistry.mCount != 0) { 3068123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik try { 3078123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG); 3088123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerView.mState.prepareForNestedPrefetch(innerView.mAdapter); 3098123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) { 3108123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // Note that we ignore immediate flag for inner items because 3118123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // we have lower confidence they're needed next frame. 3128123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i]; 3138123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs); 314945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 3158123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } finally { 3168123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik TraceCompat.endSection(); 317a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 3181e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3191e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3201e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 3218123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private void flushTaskWithDeadline(Task task, long deadlineNs) { 3228123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs; 3238123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view, 3248123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik task.position, taskDeadlineNs); 3258123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (holder != null && holder.mNestedRecyclerView != null) { 3268123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs); 3278123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3288123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3298123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 3301e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private void flushTasksWithDeadline(long deadlineNs) { 3311e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik for (int i = 0; i < mTasks.size(); i++) { 3321e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final Task task = mTasks.get(i); 3331e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (task.view == null) { 3341e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik break; // done with populated tasks 3351e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3361e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik flushTaskWithDeadline(task, deadlineNs); 337356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.clear(); 338a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 33907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 34007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 341356880d3de117b067522ad452f4e3eed85ce444cChris Craik void prefetch(long deadlineNs) { 342356880d3de117b067522ad452f4e3eed85ce444cChris Craik buildTaskList(); 343356880d3de117b067522ad452f4e3eed85ce444cChris Craik flushTasksWithDeadline(deadlineNs); 344356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 345356880d3de117b067522ad452f4e3eed85ce444cChris Craik 34607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik @Override 34707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void run() { 34807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik try { 34907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG); 35007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 35107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mRecyclerViews.isEmpty()) { 35207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // abort - no work to do 35307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 35407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 35507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 35607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // Query last vsync so we can predict next one. Note that drawing time not yet 35707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // valid in animation/input callbacks, so query it here to be safe. 358356880d3de117b067522ad452f4e3eed85ce444cChris Craik long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos( 35907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerViews.get(0).getDrawingTime()); 360356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (lastFrameVsyncNs == 0) { 36107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // abort - couldn't get last vsync for estimating next 36207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 36307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 36407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 365a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // TODO: consider rebasing deadline if frame was already dropped due to long UI work. 366a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik // Next frame will still wait for VSYNC, so we can still use the gap if it exists. 367356880d3de117b067522ad452f4e3eed85ce444cChris Craik long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs; 368356880d3de117b067522ad452f4e3eed85ce444cChris Craik 369356880d3de117b067522ad452f4e3eed85ce444cChris Craik prefetch(nextFrameNs); 37007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 37107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // TODO: consider rescheduling self, if there's more work to do 37207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } finally { 37307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mPostTimeNs = 0; 37407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.endSection(); 37507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 37607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 37707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik} 378