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