GapWorker.java revision 7149a63961c5fe6706160bc717a3b6cbb083ca54
17149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas/* 27149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Copyright (C) 2017 The Android Open Source Project 37149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 47149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Licensed under the Apache License, Version 2.0 (the "License"); 57149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * you may not use this file except in compliance with the License. 67149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * You may obtain a copy of the License at 77149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 87149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * http://www.apache.org/licenses/LICENSE-2.0 97149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * 107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Unless required by applicable law or agreed to in writing, software 117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * distributed under the License is distributed on an "AS IS" BASIS, 127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * See the License for the specific language governing permissions and 147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * limitations under the License. 157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikaspackage com.android.internal.widget; 187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.annotation.Nullable; 207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.os.Trace; 217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport android.view.View; 227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.ArrayList; 247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Arrays; 257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Collections; 267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.Comparator; 277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasimport java.util.concurrent.TimeUnit; 287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikasfinal class GapWorker implements Runnable { 307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>(); 327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>(); 347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long mPostTimeNs; 357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long mFrameIntervalNs; 367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static class Task { 387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public boolean immediate; 397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int viewVelocity; 407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int distanceToItem; 417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public RecyclerView view; 427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int position; 437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void clear() { 457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas immediate = false; 467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas viewVelocity = 0; 477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas distanceToItem = 0; 487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas view = null; 497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas position = 0; 507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects 557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared 567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * in between calls. 577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private ArrayList<Task> mTasks = new ArrayList<>(); 597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Prefetch information associated with a specific RecyclerView. 627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static class LayoutPrefetchRegistryImpl 647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas implements RecyclerView.LayoutManager.LayoutPrefetchRegistry { 657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mPrefetchDx; 667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mPrefetchDy; 677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int[] mPrefetchArray; 687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int mCount; 707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void setPrefetchVector(int dx, int dy) { 727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchDx = dx; 737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchDy = dy; 747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void collectPrefetchPositionsFromView(RecyclerView view, boolean nested) { 777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCount = 0; 787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPrefetchArray != null) { 797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Arrays.fill(mPrefetchArray, -1); 807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final RecyclerView.LayoutManager layout = view.mLayout; 837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (view.mAdapter != null 847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && layout != null 857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && layout.isItemPrefetchEnabled()) { 867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (nested) { 877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // nested prefetch, only if no adapter updates pending. Note: we don't query 887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // view.hasPendingAdapterUpdates(), as first layout may not have occurred 897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!view.mAdapterHelper.hasPendingUpdates()) { 907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layout.collectInitialPrefetchPositions(view.mAdapter.getItemCount(), this); 917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // momentum based prefetch, only if we trust current child/adapter state 947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (!view.hasPendingAdapterUpdates()) { 957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy, 967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas view.mState, this); 977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mCount > layout.mPrefetchMaxCountObserved) { 1017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layout.mPrefetchMaxCountObserved = mCount; 1027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas layout.mPrefetchMaxObservedInInitialPrefetch = nested; 1037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas view.mRecycler.updateViewCacheSize(); 1047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 1097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void addPosition(int layoutPosition, int pixelDistance) { 1107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (pixelDistance < 0) { 1117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalArgumentException("Pixel distance must be non-negative"); 1127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // allocate or expand array as needed, doubling when needed 1157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int storagePosition = mCount * 2; 1167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPrefetchArray == null) { 1177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchArray = new int[4]; 1187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Arrays.fill(mPrefetchArray, -1); 1197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else if (storagePosition >= mPrefetchArray.length) { 1207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int[] oldArray = mPrefetchArray; 1217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchArray = new int[storagePosition * 2]; 1227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length); 1237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // add position 1267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchArray[storagePosition] = layoutPosition; 1277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPrefetchArray[storagePosition + 1] = pixelDistance; 1287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mCount++; 1307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean lastPrefetchIncludedPosition(int position) { 1337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPrefetchArray != null) { 1347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int count = mCount * 2; 1357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < count; i += 2) { 1367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPrefetchArray[i] == position) return true; 1377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 1407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Called when prefetch indices are no longer valid for cache prioritization. 1447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void clearPrefetchPositions() { 1467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPrefetchArray != null) { 1477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Arrays.fill(mPrefetchArray, -1); 1487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void add(RecyclerView recyclerView) { 1537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) { 1547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalStateException("RecyclerView already present in worker list!"); 1557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mRecyclerViews.add(recyclerView); 1577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void remove(RecyclerView recyclerView) { 1607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas boolean removeSuccess = mRecyclerViews.remove(recyclerView); 1617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (RecyclerView.DEBUG && !removeSuccess) { 1627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalStateException("RecyclerView removal failed!"); 1637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas /** 1677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas * Schedule a prefetch immediately after the current traversal. 1687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas */ 1697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) { 1707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (recyclerView.isAttachedToWindow()) { 1717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) { 1727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas throw new IllegalStateException("attempting to post unregistered view!"); 1737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mPostTimeNs == 0) { 1757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPostTimeNs = recyclerView.getNanoTime(); 1767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recyclerView.post(this); 1777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy); 1817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static Comparator<Task> sTaskComparator = new Comparator<Task>() { 1847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 1857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public int compare(Task lhs, Task rhs) { 1867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // first, prioritize non-cleared tasks 1877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if ((lhs.view == null) != (rhs.view == null)) { 1887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return lhs.view == null ? 1 : -1; 1897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // then prioritize immediate 1927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (lhs.immediate != rhs.immediate) { 1937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return lhs.immediate ? -1 : 1; 1947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 1957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 1967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // then prioritize _highest_ view velocity 1977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity; 1987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (deltaViewVelocity != 0) return deltaViewVelocity; 1997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // then prioritize _lowest_ distance to item 2017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem; 2027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (deltaDistanceToItem != 0) return deltaDistanceToItem; 2037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return 0; 2057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas }; 2077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void buildTaskList() { 2097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Update PrefetchRegistry in each view 2107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int viewCount = mRecyclerViews.size(); 2117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int totalTaskCount = 0; 2127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < viewCount; i++) { 2137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView view = mRecyclerViews.get(i); 2147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas view.mPrefetchRegistry.collectPrefetchPositionsFromView(view, false); 2157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas totalTaskCount += view.mPrefetchRegistry.mCount; 2167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Populate task list from prefetch data... 2197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mTasks.ensureCapacity(totalTaskCount); 2207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int totalTaskIndex = 0; 2217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < viewCount; i++) { 2227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView view = mRecyclerViews.get(i); 2237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas LayoutPrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry; 2247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx) 2257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas + Math.abs(prefetchRegistry.mPrefetchDy); 2267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) { 2277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final Task task; 2287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (totalTaskIndex >= mTasks.size()) { 2297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task = new Task(); 2307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mTasks.add(task); 2317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 2327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task = mTasks.get(totalTaskIndex); 2337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1]; 2357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.immediate = distanceToItem <= viewVelocity; 2377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.viewVelocity = viewVelocity; 2387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.distanceToItem = distanceToItem; 2397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.view = view; 2407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.position = prefetchRegistry.mPrefetchArray[j]; 2417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas totalTaskIndex++; 2437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // ... and priority sort 2477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Collections.sort(mTasks, sTaskComparator); 2487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas static boolean isPrefetchPositionAttached(RecyclerView view, int position) { 2517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int childCount = view.mChildHelper.getUnfilteredChildCount(); 2527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < childCount; i++) { 2537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas View attachedView = view.mChildHelper.getUnfilteredChildAt(i); 2547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.ViewHolder holder = RecyclerView.getChildViewHolderInt(attachedView); 2557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Note: can use mPosition here because adapter doesn't have pending updates 2567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (holder.mPosition == position && !holder.isInvalid()) { 2577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return true; 2587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return false; 2617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private RecyclerView.ViewHolder prefetchPositionWithDeadline(RecyclerView view, 2647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas int position, long deadlineNs) { 2657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (isPrefetchPositionAttached(view, position)) { 2667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // don't attempt to prefetch attached views 2677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return null; 2687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.Recycler recycler = view.mRecycler; 2717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline( 2727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas position, false, deadlineNs); 2737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (holder != null) { 2757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (holder.isBound()) { 2767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Only give the view a chance to go into the cache if binding succeeded 2777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Note that we must use public method, since item may need cleanup 2787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycler.recycleView(holder.itemView); 2797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } else { 2807149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Didn't bind, so we can't cache the view, but it will stay in the pool until 2817149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // next prefetch/traversal. If a View fails to bind, it means we didn't have 2827149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // enough time prior to the deadline (and won't for other instances of this 2837149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // type, during this GapWorker prefetch pass). 2847149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas recycler.addViewHolderToRecycledViewPool(holder, false); 2857149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2867149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2877149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return holder; 2887149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2897149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2907149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void prefetchInnerRecyclerViewWithDeadline(@Nullable RecyclerView innerView, 2917149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long deadlineNs) { 2927149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (innerView == null) { 2937149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 2947149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 2957149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 2967149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (innerView.mDataSetHasChangedAfterLayout 2977149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas && innerView.mChildHelper.getUnfilteredChildCount() != 0) { 2987149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // RecyclerView has new data, but old attached views. Clear everything, so that 2997149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // we can prefetch without partially stale data. 3007149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas innerView.removeAndRecycleViews(); 3017149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3027149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3037149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // do nested prefetch! 3047149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final LayoutPrefetchRegistryImpl innerPrefetchRegistry = innerView.mPrefetchRegistry; 3057149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas innerPrefetchRegistry.collectPrefetchPositionsFromView(innerView, true); 3067149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3077149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (innerPrefetchRegistry.mCount != 0) { 3087149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas try { 3097149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Trace.beginSection(RecyclerView.TRACE_NESTED_PREFETCH_TAG); 3107149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas innerView.mState.prepareForNestedPrefetch(innerView.mAdapter); 3117149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < innerPrefetchRegistry.mCount * 2; i += 2) { 3127149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Note that we ignore immediate flag for inner items because 3137149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // we have lower confidence they're needed next frame. 3147149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final int innerPosition = innerPrefetchRegistry.mPrefetchArray[i]; 3157149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas prefetchPositionWithDeadline(innerView, innerPosition, deadlineNs); 3167149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3177149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } finally { 3187149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Trace.endSection(); 3197149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3207149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3217149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3227149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3237149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void flushTaskWithDeadline(Task task, long deadlineNs) { 3247149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long taskDeadlineNs = task.immediate ? RecyclerView.FOREVER_NS : deadlineNs; 3257149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas RecyclerView.ViewHolder holder = prefetchPositionWithDeadline(task.view, 3267149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.position, taskDeadlineNs); 3277149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (holder != null && holder.mNestedRecyclerView != null) { 3287149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas prefetchInnerRecyclerViewWithDeadline(holder.mNestedRecyclerView.get(), deadlineNs); 3297149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3307149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3317149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3327149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas private void flushTasksWithDeadline(long deadlineNs) { 3337149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas for (int i = 0; i < mTasks.size(); i++) { 3347149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas final Task task = mTasks.get(i); 3357149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (task.view == null) { 3367149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas break; // done with populated tasks 3377149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3387149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas flushTaskWithDeadline(task, deadlineNs); 3397149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas task.clear(); 3407149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3417149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3427149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3437149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas void prefetch(long deadlineNs) { 3447149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas buildTaskList(); 3457149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas flushTasksWithDeadline(deadlineNs); 3467149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3477149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3487149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas @Override 3497149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas public void run() { 3507149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas try { 3517149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Trace.beginSection(RecyclerView.TRACE_PREFETCH_TAG); 3527149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3537149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (mRecyclerViews.isEmpty()) { 3547149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // abort - no work to do 3557149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 3567149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3577149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3587149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Query last vsync so we can predict next one. Note that drawing time not yet 3597149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // valid in animation/input callbacks, so query it here to be safe. 3607149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos( 3617149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mRecyclerViews.get(0).getDrawingTime()); 3627149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas if (lastFrameVsyncNs == 0) { 3637149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // abort - couldn't get last vsync for estimating next 3647149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas return; 3657149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3667149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3677149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // TODO: consider rebasing deadline if frame was already dropped due to long UI work. 3687149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // Next frame will still wait for VSYNC, so we can still use the gap if it exists. 3697149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs; 3707149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3717149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas prefetch(nextFrameNs); 3727149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas 3737149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas // TODO: consider rescheduling self, if there's more work to do 3747149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } finally { 3757149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas mPostTimeNs = 0; 3767149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas Trace.endSection(); 3777149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3787149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas } 3797149a63961c5fe6706160bc717a3b6cbb083ca54Aurimas Liutikas} 380