GapWorker.java revision 356880d3de117b067522ad452f4e3eed85ce444c
107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik/*
207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Copyright (C) 2016 The Android Open Source Project
307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Licensed under the Apache License, Version 2.0 (the "License");
507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * you may not use this file except in compliance with the License.
607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * You may obtain a copy of the License at
707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *      http://www.apache.org/licenses/LICENSE-2.0
907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik *
1007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * Unless required by applicable law or agreed to in writing, software
1107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * distributed under the License is distributed on an "AS IS" BASIS,
1207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * See the License for the specific language governing permissions and
1407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik * limitations under the License.
1507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik */
1607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikpackage android.support.v7.widget;
1707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
1807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport android.support.v4.os.TraceCompat;
1907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
2007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.ArrayList;
2107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.Arrays;
22356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Collections;
23356880d3de117b067522ad452f4e3eed85ce444cChris Craikimport java.util.Comparator;
2407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikimport java.util.concurrent.TimeUnit;
2507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
2607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craikclass GapWorker implements Runnable {
2707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    static final ThreadLocal<GapWorker> sGapWorker = new ThreadLocal<>();
2807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
29356880d3de117b067522ad452f4e3eed85ce444cChris Craik    ArrayList<RecyclerView> mRecyclerViews = new ArrayList<>();
3007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    long mPostTimeNs;
3107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    long mFrameIntervalNs;
3207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
33356880d3de117b067522ad452f4e3eed85ce444cChris Craik    static class Task {
34356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public boolean immediate;
35356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public int viewVelocity;
36356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public int distanceToItem;
37356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public RecyclerView view;
38356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public int position;
39356880d3de117b067522ad452f4e3eed85ce444cChris Craik
40356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public void clear() {
41356880d3de117b067522ad452f4e3eed85ce444cChris Craik            immediate = false;
42356880d3de117b067522ad452f4e3eed85ce444cChris Craik            viewVelocity = 0;
43356880d3de117b067522ad452f4e3eed85ce444cChris Craik            distanceToItem = 0;
44356880d3de117b067522ad452f4e3eed85ce444cChris Craik            view = null;
45356880d3de117b067522ad452f4e3eed85ce444cChris Craik            position = 0;
46356880d3de117b067522ad452f4e3eed85ce444cChris Craik        }
47356880d3de117b067522ad452f4e3eed85ce444cChris Craik    }
48356880d3de117b067522ad452f4e3eed85ce444cChris Craik
49356880d3de117b067522ad452f4e3eed85ce444cChris Craik    /**
50356880d3de117b067522ad452f4e3eed85ce444cChris Craik     * Temporary storage for prefetch Tasks that execute in {@link #prefetch(long)}. Task objects
51356880d3de117b067522ad452f4e3eed85ce444cChris Craik     * are pooled in the ArrayList, and never removed to avoid allocations, but always cleared
52356880d3de117b067522ad452f4e3eed85ce444cChris Craik     * in between calls.
53356880d3de117b067522ad452f4e3eed85ce444cChris Craik     */
54356880d3de117b067522ad452f4e3eed85ce444cChris Craik    private ArrayList<Task> mTasks = new ArrayList<>();
55356880d3de117b067522ad452f4e3eed85ce444cChris Craik
56945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik    /**
57945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik     * Prefetch information associated with a specfic RecyclerView.
58945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik     */
59945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik    static class PrefetchRegistryImpl implements RecyclerView.PrefetchRegistry {
60945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        private int mPrefetchDx;
61945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        private int mPrefetchDy;
62945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        int[] mPrefetchArray;
63945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
64945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        int mCount;
65945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
66945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        void setPrefetchVector(int dx, int dy) {
67945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mPrefetchDx = dx;
68945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mPrefetchDy = dy;
69945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
70945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
71945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        void collectPrefetchPositionsFromView(RecyclerView view) {
72945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mCount = 0;
73945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (mPrefetchArray != null) {
74945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                Arrays.fill(mPrefetchArray, -1);
75945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
76945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
77945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            final RecyclerView.LayoutManager layout = view.mLayout;
78945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (view.mAdapter != null
79945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    && layout != null
80945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    && layout.isItemPrefetchEnabled()
81945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    && !view.hasPendingAdapterUpdates()) {
82945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                layout.collectPrefetchPositions(mPrefetchDx, mPrefetchDy, view.mState, this);
83945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                if (mCount > layout.mPrefetchMaxCountObserved) {
84945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    layout.mPrefetchMaxCountObserved = mCount;
85945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    view.mRecycler.updateViewCacheSize();
86945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                }
87945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
88945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
89945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
90945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        @Override
91945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        public void addPosition(int layoutPosition, int pixelDistance) {
92945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (pixelDistance < 0) {
93945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                throw new IllegalArgumentException("Pixel distance must be non-negative");
94945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
95945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
96945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            // allocate or expand array as needed, doubling when needed
97945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            final int storagePosition = mCount * 2;
98945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (mPrefetchArray == null) {
99945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                mPrefetchArray = new int[4];
100945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                Arrays.fill(mPrefetchArray, -1);
101945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            } else if (storagePosition >= mPrefetchArray.length) {
102945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                final int[] oldArray = mPrefetchArray;
103945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                mPrefetchArray = new int[storagePosition * 2];
104945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);
105945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
106945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
107945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            // add position
108945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mPrefetchArray[storagePosition] = layoutPosition;
109945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mPrefetchArray[storagePosition + 1] = pixelDistance;
110945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
111945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            mCount++;
112945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
113945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
114945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        boolean lastPrefetchIncludedPosition(int position) {
115945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (mPrefetchArray != null) {
116945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                final int count = mCount * 2;
117945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                for (int i = 0; i < count; i += 2) {
118945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    if (mPrefetchArray[i] == position) return true;
119945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                }
120945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
121945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            return false;
122945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
123945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
124945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        /**
125945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik         * Called when prefetch indices are no longer valid for cache prioritization.
126945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik         */
127945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        void clearPrefetchPositions() {
128945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (mPrefetchArray != null) {
129945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                Arrays.fill(mPrefetchArray, -1);
130945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
131945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        }
132945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik    }
133945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik
13407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void add(RecyclerView recyclerView) {
13507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (RecyclerView.DEBUG && mRecyclerViews.contains(recyclerView)) {
13607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            throw new IllegalStateException("RecyclerView already present in worker list!");
13707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
13807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        mRecyclerViews.add(recyclerView);
13907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
14007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
14107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void remove(RecyclerView recyclerView) {
14207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        boolean removeSuccess = mRecyclerViews.remove(recyclerView);
14307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (RecyclerView.DEBUG && !removeSuccess) {
14407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            throw new IllegalStateException("RecyclerView removal failed!");
14507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
14607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
14707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
14807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    /**
14907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik     * Schedule a prefetch immediately after the current traversal.
15007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik     */
15107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    void postFromTraversal(RecyclerView recyclerView, int prefetchDx, int prefetchDy) {
15207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        if (recyclerView.isAttachedToWindow()) {
15307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (RecyclerView.DEBUG && !mRecyclerViews.contains(recyclerView)) {
15407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                throw new IllegalStateException("attempting to post unregistered view!");
15507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
15607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (mPostTimeNs == 0) {
157a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik                mPostTimeNs = recyclerView.getNanoTime();
15807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                recyclerView.post(this);
15907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
16007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
16107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
162945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik        recyclerView.mPrefetchRegistry.setPrefetchVector(prefetchDx, prefetchDy);
16307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
16407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
165356880d3de117b067522ad452f4e3eed85ce444cChris Craik    static Comparator<Task> sTaskComparator = new Comparator<Task>() {
166356880d3de117b067522ad452f4e3eed85ce444cChris Craik        @Override
167356880d3de117b067522ad452f4e3eed85ce444cChris Craik        public int compare(Task lhs, Task rhs) {
168356880d3de117b067522ad452f4e3eed85ce444cChris Craik            // first, prioritize non-cleared tasks
169356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if ((lhs.view == null) != (rhs.view == null)) {
170356880d3de117b067522ad452f4e3eed85ce444cChris Craik                return lhs.view == null ? 1 : -1;
171356880d3de117b067522ad452f4e3eed85ce444cChris Craik            }
172356880d3de117b067522ad452f4e3eed85ce444cChris Craik
173356880d3de117b067522ad452f4e3eed85ce444cChris Craik            // then prioritize immediate
174356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if (lhs.immediate != rhs.immediate) {
175356880d3de117b067522ad452f4e3eed85ce444cChris Craik                return lhs.immediate ? -1 : 1;
176356880d3de117b067522ad452f4e3eed85ce444cChris Craik            }
177356880d3de117b067522ad452f4e3eed85ce444cChris Craik
178356880d3de117b067522ad452f4e3eed85ce444cChris Craik            // then prioritize _highest_ view velocity
179356880d3de117b067522ad452f4e3eed85ce444cChris Craik            int deltaViewVelocity = rhs.viewVelocity - lhs.viewVelocity;
180356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if (deltaViewVelocity != 0) return deltaViewVelocity;
181356880d3de117b067522ad452f4e3eed85ce444cChris Craik
182356880d3de117b067522ad452f4e3eed85ce444cChris Craik            // then prioritize _lowest_ distance to item
183356880d3de117b067522ad452f4e3eed85ce444cChris Craik            int deltaDistanceToItem = lhs.distanceToItem - rhs.distanceToItem;
184356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if (deltaDistanceToItem != 0) return deltaDistanceToItem;
185356880d3de117b067522ad452f4e3eed85ce444cChris Craik
186356880d3de117b067522ad452f4e3eed85ce444cChris Craik            return 0;
187356880d3de117b067522ad452f4e3eed85ce444cChris Craik        }
188356880d3de117b067522ad452f4e3eed85ce444cChris Craik    };
189356880d3de117b067522ad452f4e3eed85ce444cChris Craik
190356880d3de117b067522ad452f4e3eed85ce444cChris Craik    private void buildTaskList() {
191356880d3de117b067522ad452f4e3eed85ce444cChris Craik        // Update PrefetchRegistry in each view
192356880d3de117b067522ad452f4e3eed85ce444cChris Craik        final int viewCount = mRecyclerViews.size();
193356880d3de117b067522ad452f4e3eed85ce444cChris Craik        int totalTaskCount = 0;
194356880d3de117b067522ad452f4e3eed85ce444cChris Craik        for (int i = 0; i < viewCount; i++) {
195356880d3de117b067522ad452f4e3eed85ce444cChris Craik            RecyclerView view = mRecyclerViews.get(i);
196356880d3de117b067522ad452f4e3eed85ce444cChris Craik            view.mPrefetchRegistry.collectPrefetchPositionsFromView(view);
197356880d3de117b067522ad452f4e3eed85ce444cChris Craik            totalTaskCount += view.mPrefetchRegistry.mCount;
198356880d3de117b067522ad452f4e3eed85ce444cChris Craik        }
199356880d3de117b067522ad452f4e3eed85ce444cChris Craik
200356880d3de117b067522ad452f4e3eed85ce444cChris Craik        // Populate task list from prefetch data...
201356880d3de117b067522ad452f4e3eed85ce444cChris Craik        mTasks.ensureCapacity(totalTaskCount);
202356880d3de117b067522ad452f4e3eed85ce444cChris Craik        int totalTaskIndex = 0;
203356880d3de117b067522ad452f4e3eed85ce444cChris Craik        for (int i = 0; i < viewCount; i++) {
204356880d3de117b067522ad452f4e3eed85ce444cChris Craik            RecyclerView view = mRecyclerViews.get(i);
205356880d3de117b067522ad452f4e3eed85ce444cChris Craik            PrefetchRegistryImpl prefetchRegistry = view.mPrefetchRegistry;
206356880d3de117b067522ad452f4e3eed85ce444cChris Craik            final int viewVelocity = Math.abs(prefetchRegistry.mPrefetchDx)
207356880d3de117b067522ad452f4e3eed85ce444cChris Craik                    + Math.abs(prefetchRegistry.mPrefetchDy);
208356880d3de117b067522ad452f4e3eed85ce444cChris Craik            for (int j = 0; j < prefetchRegistry.mCount * 2; j += 2) {
209356880d3de117b067522ad452f4e3eed85ce444cChris Craik                final Task task;
210356880d3de117b067522ad452f4e3eed85ce444cChris Craik                if (totalTaskIndex >= mTasks.size()) {
211356880d3de117b067522ad452f4e3eed85ce444cChris Craik                    task = new Task();
212356880d3de117b067522ad452f4e3eed85ce444cChris Craik                    mTasks.add(task);
213356880d3de117b067522ad452f4e3eed85ce444cChris Craik                } else {
214356880d3de117b067522ad452f4e3eed85ce444cChris Craik                    task = mTasks.get(totalTaskIndex);
215356880d3de117b067522ad452f4e3eed85ce444cChris Craik                }
216356880d3de117b067522ad452f4e3eed85ce444cChris Craik                final int distanceToItem = prefetchRegistry.mPrefetchArray[j + 1];
217356880d3de117b067522ad452f4e3eed85ce444cChris Craik
218356880d3de117b067522ad452f4e3eed85ce444cChris Craik                task.immediate = distanceToItem <= viewVelocity;
219356880d3de117b067522ad452f4e3eed85ce444cChris Craik                task.viewVelocity = viewVelocity;
220356880d3de117b067522ad452f4e3eed85ce444cChris Craik                task.distanceToItem = distanceToItem;
221356880d3de117b067522ad452f4e3eed85ce444cChris Craik                task.view = view;
222356880d3de117b067522ad452f4e3eed85ce444cChris Craik                task.position = prefetchRegistry.mPrefetchArray[j];
223356880d3de117b067522ad452f4e3eed85ce444cChris Craik
224356880d3de117b067522ad452f4e3eed85ce444cChris Craik                totalTaskIndex++;
225356880d3de117b067522ad452f4e3eed85ce444cChris Craik            }
226356880d3de117b067522ad452f4e3eed85ce444cChris Craik        }
22707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
228356880d3de117b067522ad452f4e3eed85ce444cChris Craik        // ... and priority sort
229356880d3de117b067522ad452f4e3eed85ce444cChris Craik        Collections.sort(mTasks, sTaskComparator);
230356880d3de117b067522ad452f4e3eed85ce444cChris Craik    }
231a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik
232356880d3de117b067522ad452f4e3eed85ce444cChris Craik    private void flushTasksWithDeadline(long deadlineNs) {
233356880d3de117b067522ad452f4e3eed85ce444cChris Craik        for (int i = 0; i < mTasks.size(); i++) {
234356880d3de117b067522ad452f4e3eed85ce444cChris Craik            final Task task = mTasks.get(i);
235356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if (task.view == null) {
236356880d3de117b067522ad452f4e3eed85ce444cChris Craik                // abort, only empty Tasks left
237356880d3de117b067522ad452f4e3eed85ce444cChris Craik                return;
238945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            }
239a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik
240356880d3de117b067522ad452f4e3eed85ce444cChris Craik            RecyclerView.Recycler recycler = task.view.mRecycler;
241945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            RecyclerView.ViewHolder holder = recycler.tryGetViewHolderForPositionByDeadline(
242356880d3de117b067522ad452f4e3eed85ce444cChris Craik                    task.position, false, task.immediate ? RecyclerView.FOREVER_NS : deadlineNs);
243356880d3de117b067522ad452f4e3eed85ce444cChris Craik
244945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik            if (holder != null) {
245945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                if (holder.isBound()) {
246945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    // Only give the view a chance to go into the cache if binding succeeded
247945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    recycler.recycleViewHolderInternal(holder);
248945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                } else {
249945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    // Didn't bind, so we can't cache the view, but it will stay in the pool until
250945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    // next prefetch/traversal. If a View fails to bind, it means we didn't have
251945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    // enough time prior to the deadline (and won't for other instances of this
252945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    // type, during this GapWorker prefetch pass).
253945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                    recycler.addViewHolderToRecycledViewPool(holder);
254945dc1e8a9ee0b71e6b2454ccb3e61145d2132f5Chris Craik                }
255a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            }
256356880d3de117b067522ad452f4e3eed85ce444cChris Craik            task.clear();
257a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik        }
25807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
25907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
260356880d3de117b067522ad452f4e3eed85ce444cChris Craik
261356880d3de117b067522ad452f4e3eed85ce444cChris Craik    void prefetch(long deadlineNs) {
262356880d3de117b067522ad452f4e3eed85ce444cChris Craik        buildTaskList();
263356880d3de117b067522ad452f4e3eed85ce444cChris Craik        flushTasksWithDeadline(deadlineNs);
264356880d3de117b067522ad452f4e3eed85ce444cChris Craik    }
265356880d3de117b067522ad452f4e3eed85ce444cChris Craik
26607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    @Override
26707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    public void run() {
26807b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        try {
26907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            TraceCompat.beginSection(RecyclerView.TRACE_PREFETCH_TAG);
27007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
27107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            if (mRecyclerViews.isEmpty()) {
27207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                // abort - no work to do
27307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                return;
27407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
27507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
27607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // Query last vsync so we can predict next one. Note that drawing time not yet
27707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // valid in animation/input callbacks, so query it here to be safe.
278356880d3de117b067522ad452f4e3eed85ce444cChris Craik            long lastFrameVsyncNs = TimeUnit.MILLISECONDS.toNanos(
27907b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                    mRecyclerViews.get(0).getDrawingTime());
280356880d3de117b067522ad452f4e3eed85ce444cChris Craik            if (lastFrameVsyncNs == 0) {
28107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                // abort - couldn't get last vsync for estimating next
28207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik                return;
28307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            }
28407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
285a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            // TODO: consider rebasing deadline if frame was already dropped due to long UI work.
286a93a0c08b4543401d8e2df9bd4c88f26d3b72c11Chris Craik            // Next frame will still wait for VSYNC, so we can still use the gap if it exists.
287356880d3de117b067522ad452f4e3eed85ce444cChris Craik            long nextFrameNs = lastFrameVsyncNs + mFrameIntervalNs;
288356880d3de117b067522ad452f4e3eed85ce444cChris Craik
289356880d3de117b067522ad452f4e3eed85ce444cChris Craik            prefetch(nextFrameNs);
29007b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik
29107b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            // TODO: consider rescheduling self, if there's more work to do
29207b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        } finally {
29307b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            mPostTimeNs = 0;
29407b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik            TraceCompat.endSection();
29507b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik        }
29607b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik    }
29707b2e072ee7e8f424eb95abc77695dc2c5a786bbChris Craik}
298