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; 1019ad077f4ba751b392e6f4454b09c2305cdc22b0dChris Craik layout.mPrefetchMaxObservedInInitialPrefetch = nested; 102945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik view.mRecycler.updateViewCacheSize(); 103945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 104945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 105945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 106945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 107945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik @Override 108945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik public void addPosition(int layoutPosition, int pixelDistance) { 10975280f707745630eb89642e908de9becc9bf3b86Chris Craik if (layoutPosition < 0) { 1109d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik throw new IllegalArgumentException("Layout positions must be non-negative"); 11175280f707745630eb89642e908de9becc9bf3b86Chris Craik } 11275280f707745630eb89642e908de9becc9bf3b86Chris Craik 113945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (pixelDistance < 0) { 114945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik throw new IllegalArgumentException("Pixel distance must be non-negative"); 115945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 116945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 117945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // allocate or expand array as needed, doubling when needed 118945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int storagePosition = mCount * 2; 119945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray == null) { 120945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[4]; 121945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 122945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } else if (storagePosition >= mPrefetchArray.length) { 123945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int[] oldArray = mPrefetchArray; 124945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray = new int[storagePosition * 2]; 125945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length); 126945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 127945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 128945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik // add position 129945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition] = layoutPosition; 130945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mPrefetchArray[storagePosition + 1] = pixelDistance; 131945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 132945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik mCount++; 133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 134945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 135945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik boolean lastPrefetchIncludedPosition(int position) { 136945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 137945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik final int count = mCount * 2; 138945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik for (int i = 0; i < count; i += 2) { 139945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray[i] == position) return true; 140945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 141945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 142945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik return false; 143945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 144945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 145945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik /** 146945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik * Called when prefetch indices are no longer valid for cache prioritization. 147945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik */ 148945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik void clearPrefetchPositions() { 149945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik if (mPrefetchArray != null) { 150945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik Arrays.fill(mPrefetchArray, -1); 151945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 1529d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik mCount = 0; 153945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 154945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 155945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik 15607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void add(RecyclerView recyclerView) { 15707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) { 15807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView already present in worker list!"); 15907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mRecyclerViews.add(recyclerView); 16107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 16307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void remove(RecyclerView recyclerView) { 16407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik boolean removeSuccess = mRecyclerViews.remove(recyclerView); 16507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !removeSuccess) { 16607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("RecyclerView removal failed!"); 16707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 16907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 17007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik /** 17107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Schedule a prefetch immediately after the current traversal. 17207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */ 17307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { 17407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (recyclerView.isAttachedToWindow()) { 17507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { 17607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik throw new IllegalStateException("attempting to post unregistered view!"); 17707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 17807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mPostTimeNs == 0) { 179a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik mPostTimeNs = recyclerView.getNanoTime(); 18007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik recyclerView.post(this); 18107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 18207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 18307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 184945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); 18507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 18607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 187356880d3de117b067522ad452f4e3eed85ce444cChris Craik static Comparator<Task> sTaskComparator = new Comparator<Task>() { 188356880d3de117b067522ad452f4e3eed85ce444cChris Craik @Override 189356880d3de117b067522ad452f4e3eed85ce444cChris Craik public int compare(Task lhs, Task rhs) { 190356880d3de117b067522ad452f4e3eed85ce444cChris Craik // first, prioritize non-cleared tasks 191356880d3de117b067522ad452f4e3eed85ce444cChris Craik if ((lhs.view == null) != (rhs.view == null)) { 192356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.view == null ? 1 : -1; 193356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 194356880d3de117b067522ad452f4e3eed85ce444cChris Craik 195356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize immediate 196356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (lhs.immediate != rhs.immediate) { 197356880d3de117b067522ad452f4e3eed85ce444cChris Craik return lhs.immediate ? -1 : 1; 198356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 199356880d3de117b067522ad452f4e3eed85ce444cChris Craik 200356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _highest_ view velocity 201356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity; 202356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaViewVelocity != 0) return deltaViewVelocity; 203356880d3de117b067522ad452f4e3eed85ce444cChris Craik 204356880d3de117b067522ad452f4e3eed85ce444cChris Craik // then prioritize _lowest_ distance to item 205356880d3de117b067522ad452f4e3eed85ce444cChris Craik int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem; 206356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (deltaDistanceToItem != 0) return deltaDistanceToItem; 207356880d3de117b067522ad452f4e3eed85ce444cChris Craik 208356880d3de117b067522ad452f4e3eed85ce444cChris Craik return 0; 209356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 210356880d3de117b067522ad452f4e3eed85ce444cChris Craik }; 211356880d3de117b067522ad452f4e3eed85ce444cChris Craik 212356880d3de117b067522ad452f4e3eed85ce444cChris Craik private void buildTaskList() { 213356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Update PrefetchRegistry in each view 214356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewCount = mRecyclerViews.size(); 215356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskCount = 0; 216356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 217356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 218bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik if (view.getWindowVisibility() == View.VISIBLE) { 219bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false); 220bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik totalTaskCount += view.mPrefetchRegistry.mCount; 221bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik } 222356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 223356880d3de117b067522ad452f4e3eed85ce444cChris Craik 224356880d3de117b067522ad452f4e3eed85ce444cChris Craik // Populate task list from prefetch data... 225356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.ensureCapacity(totalTaskCount); 226356880d3de117b067522ad452f4e3eed85ce444cChris Craik int totalTaskIndex = 0; 227356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int i = 0; i < viewCount; i++) { 228356880d3de117b067522ad452f4e3eed85ce444cChris Craik RecyclerView view = mRecyclerViews.get(i); 2299d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik if (view.getWindowVisibility() != View.VISIBLE) { 2309d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik // Invisible view, don't bother prefetching 2319d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik continue; 2329d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik } 2339d8319a04ccdb4785ca86fac3a4ff3fc6a368647Chris Craik 2343104d446bcf3da9ffb7a761fd30f0506c30f0cc9Chris Craik LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry; 235356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx) 236356880d3de117b067522ad452f4e3eed85ce444cChris Craik + Math.abs(prefetchRegistry.mPrefetchDy); 237356880d3de117b067522ad452f4e3eed85ce444cChris Craik for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) { 238356880d3de117b067522ad452f4e3eed85ce444cChris Craik final Task task; 239356880d3de117b067522ad452f4e3eed85ce444cChris Craik if (totalTaskIndex >= mTasks.size()) { 240356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = new Task(); 241356880d3de117b067522ad452f4e3eed85ce444cChris Craik mTasks.add(task); 242356880d3de117b067522ad452f4e3eed85ce444cChris Craik } else { 243356880d3de117b067522ad452f4e3eed85ce444cChris Craik task = mTasks.get(totalTaskIndex); 244356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 245356880d3de117b067522ad452f4e3eed85ce444cChris Craik final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1]; 246356880d3de117b067522ad452f4e3eed85ce444cChris Craik 247356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.immediate = distanceToItem <= viewVelocity; 248356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.viewVelocity = viewVelocity; 249356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.distanceToItem = distanceToItem; 250356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.view = view; 251356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.position = prefetchRegistry.mPrefetchArray[j]; 252356880d3de117b067522ad452f4e3eed85ce444cChris Craik 253356880d3de117b067522ad452f4e3eed85ce444cChris Craik totalTaskIndex++; 254356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 255356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 25607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 257356880d3de117b067522ad452f4e3eed85ce444cChris Craik // ... and priority sort 258356880d3de117b067522ad452f4e3eed85ce444cChris Craik Collections.sort(mTasks, sTaskComparator); 259356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 260a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 261dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik static boolean isPrefetchPositionAttached(RecyclerView view, int position) { 262dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik final int childCount = view.mChildHelper.getUnfilteredChildCount(); 263dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik for (int i = 0; i < childCount; i++) { 264dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik View attachedView = view.mChildHelper.getUnfilteredChildAt(i); 265dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView); 266dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik // Note: can use mPosition here because adapter doesn't have pending updates 267dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik if (holder.mPosition == position && !holder.isInvalid()) { 268dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return true; 269dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 270dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 271dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik return false; 272dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 273dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 2748123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view, 2751e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik int position, long deadlineNs) { 2761e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (isPrefetchPositionAttached(view, position)) { 2771e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik // don't attempt to prefetch attached views 2781e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return null; 2791e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 280a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik 2811e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik RecyclerView.Recycler recycler = view.mRecycler; 282b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik RecyclerView.ViewHolder holder; 283b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik try { 284b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik view.onEnterLayoutOrScroll(); 285b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik holder = recycler.tryGetViewHolderForPositionByDeadline( 286b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik position, false, deadlineNs); 287b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik 288b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik if (holder != null) { 289b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik if (holder.isBound() && !holder.isInvalid()) { 290b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // Only give the view a chance to go into the cache if binding succeeded 291b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // Note that we must use public method, since item may need cleanup 292b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik recycler.recycleView(holder.itemView); 293b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik } else { 294b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // Didn't bind, so we can't cache the view, but it will stay in the pool until 295b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // next prefetch/traversal. If a View fails to bind, it means we didn't have 296b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // enough time prior to the deadline (and won't for other instances of this 297b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik // type, during this GapWorker prefetch pass). 298b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik recycler.addViewHolderToRecycledViewPool(holder, false); 299b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik } 300dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik } 301b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik } finally { 302b9d3b54b0b5fa223867cf11e47ee3f31ee474031Chris Craik view.onExitLayoutOrScroll(false); 3031e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3041e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik return holder; 3051e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 306dbf84daff4f8c5a9e1b8cfa63ab14106dbab9038Chris Craik 3078123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView, 3088123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik long deadlineNs) { 3098123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerView == null) { 3108123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik return; 3118123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3120fab45107f043a4856aad2e1fd785b396898616eChris Craik 3138123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerView.mDataSetHasChangedAfterLayout 3148123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik && innerView.mChildHelper.getUnfilteredChildCount() != 0) { 3158123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // RecyclerView has new data, but old attached views. Clear everything, so that 3168123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // we can prefetch without partially stale data. 3178123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerView.removeAndRecycleViews(); 3188123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3190fab45107f043a4856aad2e1fd785b396898616eChris Craik 3208123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // do nested prefetch! 3218123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry; 3228123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true); 3238123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 3248123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik if (innerPrefetchRegistry.mCount != 0) { 3258123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik try { 3268123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik TraceCompat.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG); 3278123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik innerView.mState.prepareForNestedPrefetch(innerView.mAdapter); 3288123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) { 3298123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // Note that we ignore immediate flag for inner items because 3308123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik // we have lower confidence they're needed next frame. 3318123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i]; 3328123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs); 333945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik } 3348123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } finally { 3358123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik TraceCompat.endSection(); 336a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 3371e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3381e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3391e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik 3408123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik private void flushTaskWithDeadline(Task task, long deadlineNs) { 3418123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs; 3428123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view, 3438123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik task.position, taskDeadlineNs); 344570ab881350c836743247140a0953f87acc21c8dChris Craik if (holder != null 345570ab881350c836743247140a0953f87acc21c8dChris Craik && holder.mNestedRecyclerView != null 346570ab881350c836743247140a0953f87acc21c8dChris Craik && holder.isBound() 347570ab881350c836743247140a0953f87acc21c8dChris Craik && !holder.isInvalid()) { 3488123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs); 3498123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3508123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik } 3518123166eaf758eacc6ba5854c9b29d99f21388d6Chris Craik 3521e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik private void flushTasksWithDeadline(long deadlineNs) { 3531e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik for (int i = 0; i < mTasks.size(); i++) { 3541e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik final Task task = mTasks.get(i); 3551e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik if (task.view == null) { 3561e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik break; // done with populated tasks 3571e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik } 3581e0a453dff8d7bb7a966d006541454c770d1ed05Chris Craik flushTaskWithDeadline(task, deadlineNs); 359356880d3de117b067522ad452f4e3eed85ce444cChris Craik task.clear(); 360a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik } 36107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 36207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 363356880d3de117b067522ad452f4e3eed85ce444cChris Craik void prefetch(long deadlineNs) { 364356880d3de117b067522ad452f4e3eed85ce444cChris Craik buildTaskList(); 365356880d3de117b067522ad452f4e3eed85ce444cChris Craik flushTasksWithDeadline(deadlineNs); 366356880d3de117b067522ad452f4e3eed85ce444cChris Craik } 367356880d3de117b067522ad452f4e3eed85ce444cChris Craik 36807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik @Override 36907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik public void run() { 37007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik try { 37107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG); 37207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 37307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik if (mRecyclerViews.isEmpty()) { 37407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // abort - no work to do 37507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 37607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 37707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 378bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik // Query most recent vsync so we can predict next one. Note that drawing time not yet 37907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // valid in animation/input callbacks, so query it here to be safe. 380bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik final int size = mRecyclerViews.size(); 381bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik long latestFrameVsyncMs = 0; 382bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik for (int i = 0; i < size; i++) { 383bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik RecyclerView view = mRecyclerViews.get(i); 384bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik if (view.getWindowVisibility() == View.VISIBLE) { 385bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik latestFrameVsyncMs = Math.max(view.getDrawingTime(), latestFrameVsyncMs); 386bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik } 387bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik } 388bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik 389bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik if (latestFrameVsyncMs == 0) { 390bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik // abort - either no views visible, or couldn't get last vsync for estimating next 39107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik return; 39207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 39307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 394bec95cedb8511e1d814181aa518512c7e7fc5cf6Chris Craik long nextFrameNs = TimeUnit.MILLISECONDS.toNanos(latestFrameVsyncMs) + mFrameIntervalNs; 395356880d3de117b067522ad452f4e3eed85ce444cChris Craik 396356880d3de117b067522ad452f4e3eed85ce444cChris Craik prefetch(nextFrameNs); 39707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik 39807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik // TODO: consider rescheduling self, if there's more work to do 39907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } finally { 40007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik mPostTimeNs = 0; 40107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik TraceCompat.endSection(); 40207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 40307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik } 40407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik} 405